vpn │ │ index.js │ ├───client // │ index.html │ paper-plane.svg │ script.js │ style.css │ TweenMax.min.js │ └───icon blue.ico orange.ico red.ico yellow.ico
const { BrowserWindow, Tray, ipcMain } = require('electron') module.exports = class VPN { constructor() { // this.root = new BrowserWindow({ title: `JS.VPN-Client`, frame: false, // transparent: true, // alwaysOnTop: true, // resizable: false, // center: true, show: false, // acceptFirstMouse: true, // width: 116, height: 116, fullscreenable: false }) // this.root.loadURL(`${__dirname}/client/index.html`) this.tray = {} // this.isReconnect = false // this.reconnect_stack = [] // this.status = false // this.cb_connect = () => {} this.cb_disconnect = () => {} } ready() { // return new Promise(res => { this.root.once('ready-to-show', res) }) } showTray() { // Tray (. Electron) Title this.tray = new Tray(`${__dirname}/icon/red.ico`) this.tray.setToolTip('') } show() { // this.root.show() } hide() { // this.root.hide() } isVisible() { // boolean - return this.root.isVisible() } onTrayClick(cb) { // Tray this.tray.on('click', cb) } onTrayRightClick(cb) { // Tray this.tray.on('right-click', cb) } center() { // this.root.center() } onDisconnect(cb) { // this.cb_disconnect = cb // VPN_DISCONNECT ( VPN) ipcMain.on('VPN_DISCONNECT', e => { cb() e.returnValue = 'ok' }) } onConnect(cb) { // this.cb_connect = cb // VPN_CONNECT ( VPN) ipcMain.on('VPN_CONNECT', (e, data) => { cb() e.returnValue = 'ok' }) } connect() { // this.cb_connect() } disconnect() { // this.cb_disconnect() } onContext(cb) { // VPN_CONTEXT ( ) ipcMain.on('VPN_CONTEXT', (e, data) => { cb(data) e.returnValue = 'ok' }) } stopReconnect() { // this.reconnect_stack.map(i => clearTimeout(i)) this.reconnect_stack = [] } reconnect(cb, time = 15000) { // this.stopReconnect() // this.reconnect_stack.push( // setTimeout(() => { // this.isReconnect = true cb(() => { this.disconnect() this.connect() }) // this.isReconnect = false }, time) ) } setStatus(animation, color) { let statusText = '' if (color == 'blue') { statusText = '' } if (color == 'yellow') { statusText = '' } if (color == 'orange') { statusText = ' ' } // Title Tray this.tray.setToolTip(statusText) // this.tray.setImage(`${__dirname}/icon/${color}.ico`) this.status = color == 'red' ? false : true; // VPN_STATUS this.root.webContents.send('VPN_STATUS', JSON.stringify({ animation, color })) } }
<!DOCTYPE html> <html lang='ru'> <head> <meta charset='UTF-8'> </head> <body> <link rel='stylesheet' type='text/css' href='style.css'> <script src='TweenMax.min.js'></script> <script src='script.js'></script> <div class='btn-border'> <div class='btn-body'> <div class='but-plane'></div> </div> </div> <script> const { ipcRenderer, remote } = require('electron') , plane = document.getElementsByClassName('but-plane')[0] , btnBorder = document.getElementsByClassName('btn-border')[0] , btnBody = document.getElementsByClassName('btn-body')[0] // const win = remote.getCurrentWindow() // - localStorage.x && win.setPosition( parseInt(localStorage.x), parseInt(localStorage.y) ) // setInterval(() => { const [x, y] = win.getPosition() localStorage.setItem('x', x) localStorage.setItem('y', y) }, 3) // document.body.addEventListener('contextmenu', e => { // localStorage.setItem('context_x', e.pageX) localStorage.setItem('context_y', e.pageY) // ipcRenderer.sendSync('VPN_CONTEXT') e.preventDefault() }); // VPN let start = false plane.addEventListener('click', () => { start = !start if (start) { // VPN ipcRenderer.sendSync('VPN_CONNECT') } else { // VPN ipcRenderer.sendSync('VPN_DISCONNECT') } }) // const statVPN = new VPNstatus(plane, btnBody, btnBorder) // statVPN.color('red') statVPN.reject() // ipcRenderer.on('VPN_STATUS', (e, data) => { const { animation, color } = JSON.parse(data) statVPN.color(color) statVPN[animation]() }) </script> </body> </html>
class VPNstatus { constructor (plane, btnBody, btnBorder) { this.plane = plane this.btnBody = btnBody this.btnBorder = btnBorder // this.speedAnimation = 0.9 this.colors = { red: { boxShadow: '1px 1px 5px 1px rgba(255, 24, 37, 0.4)', borderBackground: '#e2333d', background: '#f06069' }, blue: { background: '#60a2f0', borderBackground: '#3286e3', boxShadow: '2px 2px 5px 1px rgba(40, 138, 255, 0.4)' }, yellow: { background: '#f5cc5b', borderBackground: '#f1c131', boxShadow: '2px 2px 5px 1px rgba(255, 197, 37, 0.4)' }, orange: { background: '#f09c60', borderBackground: '#e37a32', boxShadow: '2px 2px 5px 1px rgba(255, 127, 35, 0.4)' } } this.step = { start: { left: '-40px', top: '73px' }, center: { top: '19px', left: '13px' }, end: { top: '-45px', left: '71px' } } this.status = 0 } color (value) { btnBody.style.background = this.colors[value].background btnBorder.style.background = this.colors[value].borderBackground btnBorder.style.boxShadow = this.colors[value].boxShadow } waiting () { statVPN.center_to_end(() => { this.start_to_end(() => { if (this.status == 1) { TweenMax.killAll() this.plane.style.left = this.step.start.left; this.plane.style.top = this.step.start.top; statVPN.start_to_center() this.status = 2 } }) }) } resolve () { this.status = 1 } reject () { this.status = 2 statVPN.center_to_end(() => { statVPN.start_to_center() }) } start_to_end (callback) { this.plane.style.left = this.step.start.left; this.plane.style.top = this.step.start.top; TweenMax.to(this.plane, this.speedAnimation * 2, { top: this.step.end.top, left: this.step.end.left, repeat: -1, ease: Elastic.ease, onRepeat: callback ? callback : () => {} }) } center_to_end (callback) { this.plane.style.left = this.step.center.left; this.plane.style.top = this.step.center.top; TweenMax.to(this.plane, this.speedAnimation, { top: this.step.end.top, left: this.step.end.left, ease: Elastic.ease, onComplete: callback ? callback : () => {} }) } start_to_center (callback) { this.plane.style.left = this.step.start.left; this.plane.style.top = this.step.start.top; TweenMax.to(this.plane, this.speedAnimation, { top: this.step.center.top, left: this.step.center.left, ease: Elastic.ease, onComplete: callback ? callback : () => {} }) } }
* { padding: 0; margin: 0; overflow: hidden; -webkit-app-region: drag; } body { width: 100%; height: 100vh; background: rgba(0, 0, 0, 0); } .btn-border { position: absolute; left: 1px; top: 1px; width: 111px; height: 111px; background: #e2333d; border-radius: 100%; box-shadow: 1px 1px 5px 1px rgba(255, 24, 37, 0.4); display: flex; justify-content: center; align-items: center; } .btn-body { border-radius: 100%; overflow: hidden; width: 85px; height: 85px; background: #f06069; cursor: pointer; -webkit-app-region: no-drag; } .but-plane { position: relative; background: url('paper-plane.svg'); width: 60%; height: 60%; top: 71px; left: -38px; cursor: pointer; -webkit-app-region: no-drag; }
const { app } = require('electron') , VPN = require('./../../app/components/vpn') app.on('ready', async() => { const Vpn = new VPN() // await Vpn.ready() // Vpn.show() // Tray Vpn.showTray() // Vpn.onConnect(() => { console.log('Connect') // Vpn.setStatus('waiting', 'yellow') setTimeout(() => { console.log('Connected!') // Vpn.setStatus('resolve', 'blue') }, 4000) }) // Vpn.onDisconnect(() => { console.log('Disconnect') // Vpn.setStatus('reject', 'red') }) Vpn.onContext(() => { console.log('Context menu') }) // 5 // Vpn.stopReconnect() // Vpn.reconnect(next => { console.log('Reconnect') next() }, 5000) // //stopReconnect() // Tray Vpn.onTrayClick(() => { // if (!Vpn.status) { Vpn.connect() } else { Vpn.disconnect() } }) // Tray Vpn.onTrayRightClick(() => { if (Vpn.isVisible()) { Vpn.hide() } else { Vpn.show() } }) })
Colors | Animations | Description |
---|---|---|
red | reject | Disconnect |
blue | resolve | Connection |
yellow | waiting | Expectation |
orange |
Source: https://habr.com/ru/post/429434/
All Articles