├── icon.ico
├── icon.png
├── icon.icns
├── gifs
├── fast.gif
├── use.gif
├── alpha.gif
└── start.gif
├── app
├── assets
│ ├── image
│ │ ├── icon.ico
│ │ ├── logo.png
│ │ ├── change.png
│ │ ├── delete.png
│ │ ├── icon_app.png
│ │ ├── icon_tray.png
│ │ ├── icon_default.png
│ │ └── icon_whilte.png
│ ├── pick.html
│ └── index.html
├── store.js
├── renderer
│ ├── store
│ │ └── index.js
│ ├── indexPage.js
│ ├── home
│ │ ├── appExtends.js
│ │ ├── colorHistory.js
│ │ ├── keyboard.js
│ │ └── shortKey.js
│ ├── css
│ │ ├── pick.css
│ │ └── home.css
│ └── pickPage.js
├── db.js
├── menur.js
└── main.js
├── icon.iconset
├── icon_16x16.png
├── icon_32x32.png
├── icon_128x128.png
├── icon_16x16@2x.png
├── icon_256x256.png
├── icon_32x32@2x.png
├── icon_512x512.png
├── icon_128x128@2x.png
├── icon_256x256@2x.png
└── icon_512x512@2x.png
├── .gitignore
├── package.json
├── LICENSE.md
└── README.md
/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.ico
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.png
--------------------------------------------------------------------------------
/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.icns
--------------------------------------------------------------------------------
/gifs/fast.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/gifs/fast.gif
--------------------------------------------------------------------------------
/gifs/use.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/gifs/use.gif
--------------------------------------------------------------------------------
/gifs/alpha.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/gifs/alpha.gif
--------------------------------------------------------------------------------
/gifs/start.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/gifs/start.gif
--------------------------------------------------------------------------------
/app/assets/image/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/icon.ico
--------------------------------------------------------------------------------
/app/assets/image/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/logo.png
--------------------------------------------------------------------------------
/app/assets/image/change.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/change.png
--------------------------------------------------------------------------------
/app/assets/image/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/delete.png
--------------------------------------------------------------------------------
/icon.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/icon.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/app/assets/image/icon_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/icon_app.png
--------------------------------------------------------------------------------
/app/assets/image/icon_tray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/icon_tray.png
--------------------------------------------------------------------------------
/icon.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/icon.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/icon.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/icon.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/icon.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/app/assets/image/icon_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/icon_default.png
--------------------------------------------------------------------------------
/app/assets/image/icon_whilte.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/app/assets/image/icon_whilte.png
--------------------------------------------------------------------------------
/icon.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/icon.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/icon.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArthurYung/ColorPoint/HEAD/icon.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /.idea/
3 | /.cache/
4 | /db.json/
5 | yarn.lock
6 | yarn-error.log
7 | /ignore/
8 | /pkg/
9 | package-lock.json
10 |
--------------------------------------------------------------------------------
/app/store.js:
--------------------------------------------------------------------------------
1 | const { getShort, getColor } = require('./db')
2 |
3 | const DEFAULTE_KEYS = 'DEFAULTE_KEYS'
4 | const HISTORY_COLOR = 'HISTORY_COLOR'
5 |
6 | const actions = [
7 | {
8 | type: DEFAULTE_KEYS,
9 | default: getShort()
10 | },
11 | {
12 | type: HISTORY_COLOR,
13 | default: getColor()
14 | }
15 | ]
16 |
17 |
18 |
19 | module.exports = { actions, DEFAULTE_KEYS, HISTORY_COLOR }
--------------------------------------------------------------------------------
/app/renderer/store/index.js:
--------------------------------------------------------------------------------
1 | const { remote, ipcRenderer } = require( "electron" );
2 |
3 | const mutation = (action)=>{
4 | ipcRenderer.send('connct-store-context', action)
5 | }
6 |
7 | const getter = (key) => {
8 | return remote.getGlobal( "Store" )[key]
9 | }
10 |
11 | const connect = (apps = []) => {
12 | ipcRenderer.on('connct-store-provider', (event, action) => {
13 | apps.forEach(app => {
14 | if (app.subs.includes(action.type)) {
15 | app.object.dispatch(action)
16 | }
17 | })
18 | })
19 | }
20 |
21 | module.exports = {mutation, getter, connect}
--------------------------------------------------------------------------------
/app/assets/pick.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/app/renderer/indexPage.js:
--------------------------------------------------------------------------------
1 | const { connect } = require('./store/index.js')
2 | const { ipcRenderer } = require( "electron" );
3 | const Keyboard = require('./home/keyboard')
4 | const ColorHistory = require('./home/colorHistory')
5 | const size = {width: screen.availWidth, height: screen.availHeight}
6 |
7 | const startCapture = ()=> {
8 | ipcRenderer.send('start-point', size)
9 | }
10 |
11 | ipcRenderer.on('shortcut-show', startCapture)
12 |
13 | connect([{
14 | subs: ['DEFAULTE_KEYS'],
15 | object: new Keyboard({
16 | el: document.querySelector('.mixin-keyboard'),
17 | start: startCapture
18 | })
19 | }, {
20 | subs: ['HISTORY_COLOR'],
21 | object: new ColorHistory({
22 | el: document.querySelector('.mixin-colors')
23 | })
24 | }])
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "color-point",
3 | "version": "1.0.3",
4 | "private": true,
5 | "description": "A colour picking app based on Electron",
6 | "main": "./app/main.js",
7 | "scripts": {
8 | "start": "cross-env NODE_ENV=dev electron .",
9 | "build:win": "electron-packager . Color-Point --platform=win32 --icon=./icon.ico --overwrite",
10 | "build:mac": "electron-packager . Color-Point --platform=darwin --icon=./icon.icns --overwrite --darwinDarkModeSupport=true"
11 | },
12 | "repository": "https://github.com/electron/electron-quick-start",
13 | "keywords": [
14 | "Electron",
15 | "Color",
16 | "Picker",
17 | "App"
18 | ],
19 | "author": "Bruce.Au",
20 | "license": "TIM",
21 | "devDependencies": {
22 | "cross-env": "^5.2.0",
23 | "electron": "^4.0.7",
24 | "electron-debug": "^1.5.0",
25 | "electron-packager": "^13.1.1"
26 | },
27 | "dependencies": {
28 | "desktop-screenshot": "^0.1.1",
29 | "lowdb": "^1.0.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Burce.Au
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | v1.0.0
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | By Bruce.Au
34 |
35 |
36 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/db.js:
--------------------------------------------------------------------------------
1 | const { app } = require('electron')
2 | const low = require('lowdb')
3 | const { resolve } = require('path')
4 | const FileSync = require('lowdb/adapters/FileSync')
5 | const json = resolve(app.getPath('userData'), 'db.json')
6 |
7 | const adapter = new FileSync(json)
8 | const db = low(adapter)
9 |
10 | const isMac = process.platform === 'darwin'
11 | const defaultShort = `${isMac ? 'Command' : 'Control'}+Alt+G`
12 |
13 | db.defaults({ shortcut: defaultShort, colors: [] }).write()
14 |
15 | exports.changeShort = key => {
16 | db.set('shortcut', key).write()
17 | }
18 |
19 | exports.getShort = () => {
20 | return db.get('shortcut').value()
21 | }
22 |
23 | exports.pushColor = color => {
24 | if (Array.isArray(color) && color.length === 0) {
25 | return db.set('colors', []).write()
26 | }
27 | const _COLOR_ = db.get('colors')
28 | const size = _COLOR_.size().value()
29 | if (size >= 9) {
30 | _COLOR_.remove(_COLOR_.first().value()).write()
31 | }
32 | _COLOR_.push({
33 | ID: Date.now().toString(),
34 | value: color
35 | }).write()
36 | }
37 |
38 | exports.getColor = () => {
39 | return db.get('colors').value().map(color => {
40 | return color.value
41 | })
42 | }
43 |
--------------------------------------------------------------------------------
/app/renderer/home/appExtends.js:
--------------------------------------------------------------------------------
1 | class AppExtend {
2 | find(query) {
3 | return this.view.querySelector(query)
4 | }
5 | bind() {
6 | this.view.addEventListener('click', event => {
7 | let target = event.target
8 | while(target.parentNode) {
9 | if (target === this.view || target === document.body) {
10 | break
11 | }
12 | if (target.dataset.click) {
13 | this.bindEvent().clicks[target.dataset.click](event)
14 | break
15 | }
16 | target = target.parentNode
17 | }
18 | })
19 | this.view.addEventListener('mouseenter', () => {
20 | this.view.classList.add('app-hover')
21 | })
22 | this.view.addEventListener('mouseleave', () => {
23 | this.view.classList.remove('app-hover')
24 | })
25 | }
26 | bindOthre() {
27 | const focusEvent = this.bindEvent().focus
28 | const blurEvent = this.bindEvent().blur
29 | Object.keys(focusEvent).forEach(key => {
30 | this.find(`[data-focus=${key}]`)
31 | && this.find(`[data-focus=${key}]`).addEventListener('focus', focusEvent[key])
32 | })
33 | Object.keys(blurEvent).forEach(key => {
34 | this.find(`[data-blur=${key}]`)
35 | && this.find(`[data-blur=${key}]`).addEventListener('blur', blurEvent[key])
36 | })
37 | }
38 | }
39 |
40 | module.exports = AppExtend
--------------------------------------------------------------------------------
/app/menur.js:
--------------------------------------------------------------------------------
1 | const { Menu, app, BrowserWindow } = require('electron')
2 | const { platform } = require('process')
3 | const isMac = platform === 'darwin'
4 |
5 |
6 | exports.menuBuild = (main, start) => {
7 | trayTemplate = [
8 | {
9 | label: 'Start',
10 | click: start
11 | },
12 | {
13 | label: 'Show',
14 | click () {
15 | main.show()
16 | }
17 | },
18 | {
19 | label: 'Help',
20 | click() {
21 | console.log(BrowserWindow.getAllWindows())
22 |
23 | }
24 | },
25 | {
26 | label: 'Quit',
27 | click () {
28 | main.destroy()
29 | }
30 | }
31 | ]
32 | return Menu.buildFromTemplate(trayTemplate)
33 | }
34 |
35 | exports.createMenu = (main) => {
36 | const template = [
37 | {
38 | label: 'Color Point',
39 | submenu: [
40 | {
41 | label: 'Help',
42 | role: 'help'
43 | },
44 | {
45 | label: 'Quit',
46 | click () {
47 | main.destroy()
48 | }
49 | }
50 | ]
51 | },
52 | {
53 | label: 'Help',
54 | role: 'help'
55 | },
56 | {
57 | label: 'Quit',
58 | click () {
59 | main.destroy()
60 | }
61 | }
62 | ]
63 | const menu = Menu.buildFromTemplate(template)
64 | if (isMac) {
65 | Menu.setApplicationMenu(menu)
66 | } else {
67 | Menu.setApplicationMenu(null)
68 | }
69 | }
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Color Point
2 |
3 | > 桌面吸色工具
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## 应用说明
15 |
16 | **一款基于Electron开发的桌面应用**
17 |
18 | 目前支持:
19 | - `RGBA色值`
20 | - `十六进制色值`
21 | - `透明度计算`
22 | - `自定义快捷键`
23 | - `取色记录`
24 |
25 |
26 | ## 下载安装
27 |
28 | [Windows](https://github.com/ArthurYung/ColorPoint/releases/download/1.0.7/Color-Point-win32-x64.7z)
29 | [MacOS](https://github.com/ArthurYung/ColorPoint/releases/download/1.0.7/Color-Point-darwin-x64.zip)
30 |
31 |
32 | ## 开始使用
33 |
34 | ### 可在主界面自定义快捷键 .
35 | 
36 |
37 | ### 点击开始按钮或使用快捷键开始吸取颜色,吸取颜色后会自动复制到剪切板 .
38 | 
39 |
40 | ### 系统托盘內也有开始按钮 .
41 | 
42 |
43 | ### 吸取颜色时左下角菜单可切换色值类型 .
44 | 当切换透明度模式时,你可以得到一个带有指定透明度的色值。
45 | **这个色值叠加到所选背景色之后的颜色和当前屏幕所选颜色相同。 即(底色rgba1 & 透明度色值rgba2) = 屏幕色rgba3**
46 | 右键可将鼠标所指颜色设置为透明度计算的底色,点击透明度左侧色块可切换底色为纯黑/纯白。
47 | 
48 |
49 | ## 开发说明
50 |
51 | 下载项目并安装依赖
52 |
53 | ```bash
54 | git clone https://github.com/ArthurYung/ColorPoint.git
55 | cd colorPoint && npm install
56 | ```
57 |
58 | 启动项目
59 | ```bash
60 | npm start
61 | ```
62 |
63 | 打包生产
64 | ```bash
65 | npm run build:mac
66 | ```
67 | **未使用热更新,如有需要可自行实现**
68 |
69 |
70 | ## License
71 |
72 | [MIT](LICENSE.md)
73 |
--------------------------------------------------------------------------------
/app/renderer/home/colorHistory.js:
--------------------------------------------------------------------------------
1 | const { clipboard, ipcRenderer } = require( "electron" );
2 | const AppExtend = require('./appExtends')
3 | const { getter, mutation } = require('../store')
4 | class ColorHistory extends AppExtend {
5 | constructor(opt) {
6 | super()
7 | this.view = opt.el || document.body
8 | this.init()
9 | }
10 | init () {
11 | this.proxy()
12 | this.bind()
13 | this._render()
14 | this.colors.render = getter('HISTORY_COLOR')
15 | }
16 | proxy () {
17 | let self = this
18 | this.colors = new Proxy({}, {
19 | set(proxy, key, value) {
20 | proxy[key] = value
21 | console.log(value)
22 | self.colorRender(value)
23 | return true
24 | },
25 | get(proxy, key) {
26 | return proxy[key]
27 | }
28 | })
29 | }
30 |
31 | dispatch() {
32 | this.colors.render = getter('HISTORY_COLOR')
33 | }
34 |
35 | _render() {
36 | this.view.innerHTML = this.render()
37 | }
38 |
39 | bindEvent() {
40 | let self = this
41 | return {
42 | clicks: {
43 | handleClick () {
44 | mutation({
45 | type: 'HISTORY_COLOR',
46 | payload: []
47 | })
48 | },
49 | chooseColor (event) {
50 | const color = event.target.dataset.color
51 | if (color) {
52 | event.stopPropagation()
53 | clipboard.writeText(color)
54 | ipcRenderer.send('show-notification')
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | colorRender(colors) {
62 | let LISTS = ''
63 | colors.forEach(color => {
64 | LISTS += ``
65 | })
66 | this.find('.color-view') && (this.find('.color-view').innerHTML = LISTS)
67 | }
68 |
69 | render() {
70 | return `
71 |
72 |
73 | History colors
74 |
75 |
76 |
77 |
`
78 | }
79 | }
80 |
81 | module.exports = ColorHistory
--------------------------------------------------------------------------------
/app/renderer/home/keyboard.js:
--------------------------------------------------------------------------------
1 | const ShortKeys = require('./shortKey')
2 | const AppExtend = require('./appExtends')
3 | const { getter, mutation } = require('../store')
4 | class KeyboardApp extends AppExtend {
5 | constructor(opt) {
6 | super()
7 | this.view = opt.el || document.body
8 | this.start = opt.start || new Function()
9 | this.shortKey = new ShortKeys()
10 | this.keyboard = getter('DEFAULTE_KEYS')
11 | this.buttonText = "Start Picking Colors"
12 | this.type = 1
13 | this.init()
14 | }
15 | init() {
16 | this.bind()
17 | this._render()
18 | this.shortKey.onkeypress(value => {
19 | if (value === '') {
20 | value = 'Shortcut keys is null'
21 | }
22 | this.keyboard = value
23 | this.find('.keyboard-view') && (this.find('.keyboard-view').value = value)
24 | })
25 | }
26 |
27 | _render() {
28 | this.view.innerHTML = this.render()
29 | this.bindOthre()
30 | }
31 | switch() {
32 | this.type = this.type === 1 ? 2 : 1
33 | this._render()
34 | }
35 | bindEvent() {
36 | let self = this
37 | return {
38 | clicks: {
39 | handleClick () {
40 | self.switch()
41 | },
42 | handleStart () {
43 | self.start()
44 | }
45 | },
46 | focus: {
47 | handleFocus (e) {
48 | e.target.parentNode.classList.add('reset-input-focus')
49 | self.shortKey.onFocus()
50 | }
51 | },
52 | blur: {
53 | handleBlur (e) {
54 | e.target.parentNode.classList.remove('reset-input-focus')
55 | self.shortKey.onBlur()
56 | mutation({
57 | type: 'DEFAULTE_KEYS',
58 | payload: self.keyboard
59 | })
60 | }
61 | }
62 | }
63 | }
64 | dispatch(action) {
65 | this.keyboard = action.payload
66 | }
67 | render() {
68 | if (this.type === 1) {
69 | return `
70 |
71 |
72 |
${this.buttonText}
73 |
${this.keyboard}
74 |
75 |
76 |
`
77 | } else {
78 | return `
79 | `
85 | }
86 | }
87 | }
88 |
89 | module.exports = KeyboardApp
--------------------------------------------------------------------------------
/app/renderer/css/pick.css:
--------------------------------------------------------------------------------
1 | body, html{
2 | overflow: hidden;
3 | background-color: rgba(0, 0, 0, 0);
4 | position: relative;
5 | }
6 |
7 | canvas {
8 | position: absolute;
9 | left: 0;
10 | bottom: 0
11 | }
12 |
13 | .clip-view {
14 | position: absolute;
15 | top: 0;
16 | left: 0;
17 | bottom: auto
18 | }
19 | .clip-view canvas {
20 | border-radius: 50%;
21 | position: absolute;
22 | top: 0;
23 | left: 0;
24 | border: 2px solid #000000;
25 | }
26 |
27 | .clip-view span {
28 | display: inline-block;
29 | position: absolute;
30 | top: 110%;
31 | left: 50%;
32 | transform: translateX(-50%);
33 | background-color: rgba(0, 0, 0, .4);
34 | line-height: 20px;
35 | font-size: 10px;
36 | color: #ffffff;
37 | padding: 0 4px;
38 | white-space:nowrap;
39 | }
40 |
41 |
42 | .menu-view {
43 | position: absolute;
44 | left: 0;
45 | bottom: 10%;
46 | background-color: rgba(255,255,255,.9);
47 | border-radius: 0 12px 12px 0;
48 | width: 160px;
49 | height: 110px;
50 | padding: 10px 0;
51 | cursor: pointer;
52 | transition: transform .4s ease-out
53 | }
54 |
55 | .hide-view {
56 | transform: translate(-120px, 0)
57 | }
58 |
59 | .menu-view a {
60 | background: none;
61 | border:none;
62 | display: block;
63 | position: absolute;
64 | width: 16px;
65 | height: 20px;
66 | right: 8px;
67 | top: 48px;
68 | text-align: center;
69 | font-size: 22px;
70 | color: #868686
71 | }
72 |
73 | .menu-view a:hover {
74 | color: #292929
75 | }
76 |
77 | .menu-view a.menu-show::before {
78 | content: '‹'
79 | }
80 |
81 | .menu-view a.menu-hide::before {
82 | content: '›'
83 | }
84 |
85 | .canvas-label {
86 | padding: 0 14px;
87 | line-height: 30px;
88 | font-size: 14px;
89 | width: 70%
90 | }
91 |
92 | .canvas-label:hover>span:first-child {
93 | border-color: #3a3a3a
94 | }
95 |
96 | .canvas-label>span:first-child {
97 | position: relative;
98 | display: block;
99 | width: 18px;
100 | height: 18px;
101 | border-radius: 50%;
102 | border: 1px solid #9a9a9a;
103 | margin-right: 8px;
104 | margin-top: 4px;
105 | float: left;
106 | }
107 |
108 | .canvas-label > span.checked-value::before {
109 | content: '';
110 | display: block;
111 | position: absolute;
112 | top: 2px;
113 | left: 2px;
114 | width: 14px;
115 | height: 14px;
116 | border-radius: 50%;
117 | background-color: #9343e9
118 | }
119 |
120 | input[type=range] {
121 | -webkit-appearance: none;
122 | width: 80px;
123 | height: 2px;
124 | border-radius: 2px; /*这个属性设置使填充进度条时的图形为圆角*/
125 | background-color: #dedede
126 | }
127 | input[type=range]::-webkit-slider-thumb {
128 | -webkit-appearance: none;
129 | appearance: none; /*//这三个是去掉滑块原有的默认样式,划重点!!*/
130 | -webkit-box-shadow:0 0 2px ; /*设置滑块的阴影*/
131 | width: 12px;
132 | height: 12px;
133 | background: #fff;
134 | border-radius: 50%;
135 | border: 2px solid #9343e9;
136 | }
137 | .range-box {
138 | position: relative;
139 | opacity: 0.2;
140 | }
141 |
142 | .is-alpha {
143 | opacity: 1;
144 | }
145 |
146 | .range-box i{
147 | display: inline-block;
148 | width: 10px;
149 | height: 10px;
150 | border: 1px solid #acacac;
151 | margin: 0 8px 0 16px;
152 | }
153 | .range-box input {
154 | position: absolute;
155 | top: 10px;
156 | left: 40px;
157 | }
158 |
159 | .range-box span {
160 | margin-left: 90px;
161 | font-size: 12px
162 | }
--------------------------------------------------------------------------------
/app/renderer/css/home.css:
--------------------------------------------------------------------------------
1 | .main-logo {
2 | display: block;
3 | margin: 30px auto 0 auto
4 | }
5 |
6 | .main-title {
7 | width: 100%;
8 | text-align: center;
9 | line-height: 34px
10 | }
11 |
12 | .main-text {
13 | font-size: 16px;
14 | color: #696a77
15 | }
16 |
17 | .article-app {
18 | width: 100%;
19 | position: relative;
20 | }
21 |
22 | .mixin-keyboard {
23 | height: 44px;
24 | margin-top: 14px;
25 | }
26 |
27 | .start-view {
28 | width: 100%;
29 | height: 100%;
30 | position: relative;
31 | overflow: hidden;
32 | }
33 |
34 | .start-btn {
35 | position: relative;
36 | width: 236px;
37 | height: 44px;
38 | margin: 0 auto;
39 | padding: 0;
40 | border-radius: 22px;
41 | background-color: #9343e9;
42 | border:none;
43 | cursor: pointer;
44 | transition: background-color .3s ease-out
45 | }
46 |
47 | .button-text,.button-juder {
48 | position: absolute;
49 | width: 100%;
50 | text-align: center;
51 | transition: transform .3s ease-out;
52 | }
53 |
54 | .button-text {
55 | top: 0px;
56 | line-height: 44px;
57 | color: #fff;
58 | font-size: 18px;
59 | font-weight: bolder;
60 | }
61 |
62 | .button-juder {
63 | top: 44px;
64 | color: rgba(255,255,255,.5);
65 | font-size: 14px
66 | }
67 |
68 | .button {
69 | border: none;
70 | border-radius: 0;
71 | margin: 0;
72 | padding: 0;
73 | background-color: rgba(0,0,0,0)
74 | }
75 |
76 | .reset-key {
77 | display: block;
78 | visibility: hidden;
79 | position: absolute;
80 | right: 50px;
81 | top: 12px;
82 | transition: opacity .3s ease-out;
83 | opacity: 0;
84 | }
85 |
86 | .icon {
87 | display: block;
88 | width: 20px;
89 | height: 20px;
90 | background-position: center;
91 | background-repeat: no-repeat;
92 | background-size: contain;
93 | cursor: pointer;
94 | }
95 |
96 | .icon-reset {
97 | background-image: url('../../assets/image/change.png')
98 | }
99 |
100 | .start-btn:hover {
101 | background-color: #a158f0
102 | }
103 |
104 | .app-hover .button-text {
105 | transform: translate(0, -8px)
106 | }
107 |
108 | .app-hover .button-juder {
109 | transform: translate(0, -20px)
110 | }
111 |
112 | .app-hover .reset-key, .app-hover .delete-all {
113 | visibility: visible;
114 | opacity: 1;
115 | }
116 |
117 | .reset-input {
118 | position: relative;
119 | display: flex;
120 | width: 230px;
121 | height: 36px;
122 | background-color: rgba(255,255,255,.8);
123 | overflow: hidden;
124 | border-radius: 18px;
125 | margin: 4px auto 0 auto;
126 | justify-content: center;
127 | transition: background-color .3s ease-out
128 | }
129 |
130 | .reset-input-focus {
131 | background-color: rgba(255,255,255,1)
132 | }
133 |
134 | .keyboard-view {
135 | position: relative;
136 | width: 100%;
137 | margin: 0;
138 | padding: 0;
139 | border:none;
140 | background-color: rgba(0,0,0,0);
141 | font-size: 16px;
142 | font-weight: bolder;
143 | color: #9343e9;
144 | text-align: center;
145 | }
146 |
147 | .delete-all {
148 | visibility: hidden;
149 | position: absolute;
150 | top: 8px;
151 | right: 120px;
152 | transition: opacity .3s ease-out;
153 | opacity: 0;
154 | }
155 |
156 | .history-view {
157 | width: 100%;
158 | height: 88px;
159 | margin-top: 36px;
160 | background-color: #292d44
161 | }
162 |
163 | .icon-delete {
164 | background-image: url('../../assets/image/delete.png');
165 | height: 18px;
166 | }
167 |
168 | .color-view {
169 | width: 100%;
170 | margin-top: 6px;
171 | text-align: center
172 | }
173 |
174 | .color-view .color-list {
175 | display: inline-block;
176 | width: 28px;
177 | height: 28px;
178 | border-radius: 50%;
179 | margin: 0 6px;
180 | box-shadow: 8px 8px 16px rgba(0,0,0,.4);
181 | transition: transform .15s ease-out;
182 | transform: scale(1);
183 | cursor: pointer;
184 | }
185 |
186 | .color-list:hover {
187 | transform: scale(1.2)
188 | }
189 |
190 | .footer {
191 | line-height: 20px
192 | }
193 |
194 | .footer .main-text {
195 | color: #2b2b4c;
196 | font-size: 12px;
197 | }
--------------------------------------------------------------------------------
/app/renderer/home/shortKey.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: A keyborad input listenter.
3 | * @Author: Bruce.Au
4 | * @LastEditors: Please set LastEditors
5 | * @Date: 2019-03-21 23:19:57
6 | * @LastEditTime: 2019-03-27 18:57:47
7 | */
8 |
9 | const isMac = /^Mac/.test(navigator.platform)
10 |
11 | const commands = ['Command', 'Control', 'Shift','Capslock', 'Alt', 'Tap', 'Enter', 'Delete', 'Backspace']
12 |
13 | const numberKeys = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
14 |
15 | const symbolKeys = [ '-', '=', '[', ']', '\\', ';', "'", ',', '.', '.', '/']
16 |
17 | const letterKeys = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
18 |
19 | const _MATCH_DIGIT_ = /^(Digit)(.*)/
20 |
21 | const _MATCH_kEY_ = /^(Key)(.*)/
22 |
23 | const _MATCH_NUMBER_ = /^(Numpad)(.*)/
24 |
25 | const _MATCH_RESOLVE_ = {
26 | MetaLeft : isMac ? 'Command' : 'Meta',
27 | MetaRight : isMac ? 'Command' : 'Meta',
28 | ControlLeft : 'Control',
29 | ControlRight : 'Control',
30 | AltLeft : 'Alt',
31 | AltRight : 'Alt',
32 | ShiftLeft : 'Shift',
33 | ShiftRight : 'Shift',
34 | Minus : '-',
35 | Equal : '=',
36 | Backslash : '\\',
37 | BracketLeft : '[',
38 | BracketRight : ']',
39 | Semicolon : ';',
40 | Quote : "'",
41 | Comma : ',',
42 | Period : '.',
43 | Slash : '/',
44 | Add : '+',
45 | Subtract : '-',
46 | Multiply : '*',
47 | Divide : '/',
48 | Decimal : 'Delete'
49 | }
50 |
51 |
52 | class ShortKeys{
53 |
54 | constructor(options = {}) {
55 | this.commands = options.command || commands
56 | this.attendDemand = options.attend || ['number', 'symbol', 'letter']
57 | this.ele = options.ele || document
58 | this.attends = []
59 | this.commandKeys = {}
60 | this.currentKeys = []
61 | this.attendKey = ''
62 | this.keyboardValue = ''
63 | this.focus = false
64 | this.onkeypressCallback = () => {}
65 | this.init()
66 | }
67 |
68 | init() {
69 | this.combination = this.Combination.bind(this)
70 | this.addEventKey()
71 | this.checkLocal()
72 | }
73 |
74 | checkLocal() {
75 | this.attendDemand.forEach(name => {
76 | if (name === 'number') {
77 | this.attends = [...this.attends, ...numberKeys]
78 | }
79 | if (name === 'symbol') {
80 | this.attends = [...this.attends, ...symbolKeys]
81 | }
82 | if (name === 'letter') {
83 | this.attends = [...this.attends, ...letterKeys]
84 | }
85 | })
86 | }
87 |
88 | matchKey (key) {
89 | if (_MATCH_DIGIT_.test(key)) {
90 | key = key.replace(_MATCH_DIGIT_, '$2')
91 | }
92 | if (_MATCH_kEY_.test(key)) {
93 | key = key.replace(_MATCH_kEY_, '$2')
94 | }
95 | if (_MATCH_NUMBER_.test(key)) {
96 | key = key.replace(_MATCH_NUMBER_, '$2')
97 | }
98 | if (_MATCH_RESOLVE_[key]) {
99 | key = _MATCH_RESOLVE_[key]
100 | }
101 | return key.toLowerCase().replace(/^[a-z]/, $1=>$1.toUpperCase());
102 | }
103 |
104 | Combination(event) {
105 | if (!this.focus) return
106 | event.preventDefault()
107 | event.returnValue=false
108 | const code = this.matchKey(event.code)
109 | const value = event.type === 'keydown'
110 | if (this.commands.includes(code)) {
111 | this.commandKeys[code] = value
112 | }
113 | if (this.attends.includes(code)) {
114 | this.attendKey = this.attendKey === code ? '' : code
115 | }
116 | value ? this.keydownEvent() : this.keyupEvent()
117 | }
118 |
119 | keydownEvent() {
120 | this.currentKeys = this.commands.filter(key => {
121 | if (this.commandKeys[key]) return true
122 | else return false
123 | })
124 | if (this.currentKeys.length > 0) {
125 | this.currentKeys.push(this.attendKey)
126 | }
127 | this.keyboardValue = this.currentKeys.join('+')
128 | this.onkeypressCallback(this.keyboardValue, this)
129 | }
130 |
131 | keyupEvent() {
132 | if (this.currentKeys[this.currentKeys.length - 1] === '') {
133 | this.clearKeyboard()
134 | this.onkeypressCallback(this.keyboardValue, this)
135 | }
136 | }
137 |
138 | clearKeyboard() {
139 | this.currentKeys = []
140 | this.commandKeys = {}
141 | this.attendKey = ''
142 | this.keyboardValue = ''
143 | }
144 |
145 | resetKeys(keys) {
146 | this.clearKeyboard()
147 | if (Array.isArray(keys)) {
148 | this.currentKeys = keys
149 | this.keyboardValue = this.currentKeys.join('+')
150 | this.onkeypressCallback(this.keyboardValue, this)
151 | }
152 | }
153 |
154 | addEventKey() {
155 | this.ele.addEventListener('keydown', this.combination)
156 | this.ele.addEventListener('keyup', this.combination)
157 | }
158 | onkeypress(fn) {
159 | if (typeof fn === 'function') {
160 | this.onkeypressCallback = fn
161 | }
162 | }
163 |
164 | destroy() {
165 | this.ele.removeEventListener('keydown', this.combination)
166 | this.ele.removeEventListener('keyup', this.combination)
167 | }
168 |
169 | onFocus() {
170 | this.focus = true
171 | }
172 |
173 | onBlur() {
174 | this.focus = false
175 | }
176 |
177 | }
178 |
179 | module.exports = ShortKeys
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const { app, BrowserWindow, ipcMain, Tray, globalShortcut, Notification } = require('electron')
3 | const { resolve } = require('path')
4 | const { createMenu, menuBuild } = require('./menur')
5 | const { changeShort, pushColor, getColor } = require('./db')
6 | const { actions, DEFAULTE_KEYS, HISTORY_COLOR } = require('./store')
7 | const ASSETS_PATH = resolve(__dirname, 'assets')
8 | const APP_ICON = resolve(ASSETS_PATH, 'image/icon_app.png')
9 | const WHILTE_ICON = resolve(ASSETS_PATH, 'image/icon_tray.png')
10 | const MAIN_HTML = resolve(ASSETS_PATH, 'index.html')
11 | const PICK_HTML = resolve(ASSETS_PATH, 'pick.html')
12 |
13 | if (process.env.NODE_ENV === 'dev') {
14 | require('electron-debug')({ showDevTools: false })
15 | }
16 |
17 | const isMac = process.platform === 'darwin'
18 |
19 | let mainWindow
20 | let pickWindow
21 | let trayApp
22 | let notification
23 | let shortcutCatch
24 |
25 | // 不同的action操作中间件
26 | const appliction = (action, event) => {
27 | switch (action.type) {
28 | case DEFAULTE_KEYS:
29 | changeShort(action.payload)
30 | let keys = action.payload.toLowerCase()
31 | setShortCut(keys)
32 | return action.payload
33 |
34 | case HISTORY_COLOR:
35 | pushColor(action.payload)
36 | return getColor()
37 |
38 | default:
39 | return action.payload
40 | }
41 | }
42 |
43 | // 注册全局快捷键
44 | function setShortCut(shortcut) {
45 | shortcut = shortcut && shortcut.toLowerCase()
46 | // 如果指令有问题,则不注册
47 | if (!shortcut || shortcut.indexOf('+') < 0) {
48 | return
49 | }
50 | // 注册之前删除上一次注册的全局快捷键
51 | if (shortcutCatch) {
52 | globalShortcut.unregister(shortcutCatch)
53 | }
54 | shortcutCatch = shortcut
55 | globalShortcut.register(shortcut, startByShort);
56 | }
57 |
58 | // 全局变量初始化
59 | function createStore (actions) {
60 | global.Store = {}
61 | actions.forEach(action => {
62 | global.Store[action.type] = action.default
63 | })
64 | }
65 |
66 | // 连接到两个window
67 | function connect (appliction) {
68 | ipcMain.on('connct-store-context', (event, action) => {
69 | global.Store[action.type] = appliction(action)
70 | mainWindow && mainWindow.webContents.send('connct-store-provider', action)
71 | pickWindow && pickWindow.webContents.send('connct-store-provider', action)
72 | })
73 | }
74 |
75 | // 创建tray
76 | function createtTray(icon) {
77 | let trayMenu = menuBuild(mainWindow, startByShort)
78 | trayApp = new Tray(icon)
79 | trayApp.setContextMenu(trayMenu)
80 | trayApp.on('right-click', () => { // 右键点击
81 | if (isMac) {
82 | mainWindow.center()
83 | mainWindow.show()
84 | }
85 | })
86 | trayApp.on('click', () => { // 右键点击
87 | mainWindow.center()
88 | mainWindow.show()
89 | })
90 | }
91 |
92 | // 快捷键对应的响应事件
93 | function startByShort() {
94 | mainWindow.webContents.send('shortcut-show');
95 | }
96 |
97 | // windows下使用任务栏气泡通知
98 |
99 | function trayMessage(content) {
100 | if (isMac || !trayApp) return
101 | trayApp.displayBalloon({icon: APP_ICON, title: 'Color Point', content})
102 | }
103 |
104 | // 绑定ipc消息
105 | function ipcMessager(main) {
106 |
107 | // 开始选择颜色
108 | ipcMain.on('start-point', function(event, arg) {
109 | const mainVisble = main.isVisible()
110 | main.setPosition(arg.width, arg.height)
111 | main.hide()
112 | pickWindow.setSize(arg.width, arg.height)
113 | pickWindow.show()
114 | pickWindow.webContents.send('start-point-pr', mainVisble)
115 | })
116 |
117 | // 关闭颜色选择窗口并打开主窗口
118 | ipcMain.on('close-pick-window', function(e, type) {
119 | pickWindow.hide()
120 | if (type) {
121 | main.center()
122 | main.show()
123 | }
124 | })
125 |
126 | // 复制颜色成功显示通知窗
127 | ipcMain.on('show-notification', function() {
128 | notification.show()
129 | trayMessage('颜色已复制')
130 | })
131 | }
132 |
133 |
134 | async function createWindow () {
135 | // Create the browser window.
136 | notification = new Notification({
137 | title: 'Color Point',
138 | body: '颜色已复制',
139 | silent: true
140 | })
141 |
142 | mainWindow = new BrowserWindow({
143 | width: 400,
144 | height: isMac ? 390 : 400,
145 | resizable: false,
146 | title: 'Color Point',
147 | backgroundColor: '#111327',
148 | icon: APP_ICON,
149 | darkTheme: true,
150 | fullscreenWindowTitle: true,
151 | show: false
152 | })
153 |
154 | pickWindow = new BrowserWindow({
155 | width: 10,
156 | height: 10,
157 | x: 0,
158 | y: 0,
159 | fullscreen: !isMac || undefined,
160 | resizable: false,
161 | movable: false,
162 | skipTaskbar: true,
163 | hasShadow: true,
164 | frame: false,
165 | alwaysOnTop: true,
166 | transparent: true,
167 | show: false
168 | })
169 |
170 | ipcMessager(mainWindow)
171 | createMenu(mainWindow)
172 | createtTray(WHILTE_ICON)
173 | createStore(actions)
174 | connect(appliction)
175 | setShortCut(global.Store.DEFAULTE_KEYS)
176 |
177 | pickWindow.loadFile(PICK_HTML)
178 | mainWindow.loadFile(MAIN_HTML)
179 |
180 | mainWindow.once('ready-to-show', () => {
181 | mainWindow.show()
182 | })
183 |
184 | mainWindow.on('show', function() {
185 | !isMac && mainWindow.setSkipTaskbar(false)
186 | })
187 |
188 | mainWindow.on('close', function(event) {
189 | event.preventDefault();
190 | mainWindow.hide();
191 | !isMac && mainWindow.setSkipTaskbar(true);
192 | trayMessage('任务最小化到托盘')
193 | })
194 |
195 | mainWindow.on('closed', function (e) {
196 | pickWindow.destroy()
197 | mainWindow = null
198 | })
199 |
200 | pickWindow.on('closed', function() {
201 | pickWindow = null
202 | app.quit()
203 | })
204 | }
205 |
206 | app.on('ready', createWindow)
207 |
208 | app.on('window-all-closed', function () {
209 | if (process.platform !== 'darwin') {
210 | app.quit()
211 | }
212 | })
213 |
214 | app.on('activate', function () {
215 | if (mainWindow === null) {
216 | createWindow()
217 | }
218 | mainWindow.isVisible() || mainWindow.show()
219 | })
220 | app.setName('Color Point')
221 |
--------------------------------------------------------------------------------
/app/renderer/pickPage.js:
--------------------------------------------------------------------------------
1 | const { ipcRenderer, clipboard } = require( "electron" );
2 | const { mutation } = require('./store')
3 | const screenshots = require('desktop-screenshot');
4 | const { resolve } = require('path')
5 | const os = require('os')
6 | const fs = require('fs')
7 | const getRGB = (str) => {
8 | if (!str) return [,,,]
9 | const [r, g, b] = str.replace(/^(rgba\()(.*)(\))$/, '$2').split(',')
10 | return [r, g, b]
11 | }
12 |
13 | const colorFormat = color => {
14 | if (color >= 255) {
15 | return '+'
16 | }
17 | if (color < 0) {
18 | return '-'
19 | }
20 | return Math.round(color)
21 | }
22 |
23 | class App {
24 | constructor(opt) {
25 | this.size = {
26 | width: screen.width,
27 | height: screen.height
28 | }
29 | this.inputLabel = [{
30 | value: 1,
31 | text: 'HEX'
32 | }, {
33 | value: 2,
34 | text: 'RGBA'
35 | }, {
36 | value: 3,
37 | text: '(rgba)透明度'
38 | }]
39 | this.src = resolve(os.tmpdir(), 'screenshot.png')
40 | this.startType = false
41 | this.imgData = []
42 | this.clipData = []
43 | this.radius = 80
44 | this.range = 11
45 | this.valueType = 1
46 | this.inMenu = false
47 | this.currentColor = ''
48 | this.clipRange = Math.ceil((2 * this.radius) / this.range)
49 | this.view = opt.el || document.body
50 | this._init()
51 | }
52 | _init() {
53 | this.createCanvas()
54 | this.createMenu()
55 | this.createProxy()
56 | this.addEventListener()
57 | }
58 |
59 | createProxy() {
60 | let self = this
61 | this.alpha = new Proxy({}, {
62 | set(proxy, key, value) {
63 | self.changeRanger(key, value)
64 | proxy[key] = value
65 | return true
66 | }
67 | })
68 | }
69 |
70 | changeRanger(key, value) {
71 | if (key === 'opacity') {
72 | this.input.getElementsByTagName('span')[0].innerText = value
73 | this.input.getElementsByTagName('input')[0].value = value * 10
74 | }
75 | if (key === 'background') {
76 | this.input.getElementsByTagName('i')[0].style.background = value
77 | }
78 | }
79 |
80 | createCanvas() {
81 | this.image = new Image()
82 | this.background = document.createElement('canvas')
83 | this.clipView = document.createElement('div')
84 | this.colorValue = document.createElement('span')
85 | this.canvas = document.createElement('canvas')
86 | this.bg = this.background.getContext('2d')
87 | this.ctx = this.canvas.getContext('2d')
88 |
89 |
90 | this.clipView.className = 'clip-view'
91 | this.clipView.style.width = `${2 * this.radius}px`
92 | this.clipView.style.height = `${2 * this.radius}px`
93 | this.clipView.style.left = `-${this.radius}px`
94 | this.clipView.style.top = `-${this.radius}px`
95 | this.clipView.appendChild(this.canvas)
96 | this.clipView.appendChild(this.colorValue)
97 | }
98 |
99 | createMenu() {
100 | this.menu = document.createElement('div')
101 | this.button = document.createElement('a')
102 | this.button.className = 'menu-show'
103 | this.menu.className = 'menu-view'
104 | this.labels = this.createLable()
105 | this.input = this.createRanger()
106 | this.labels.forEach(label => {
107 | this.menu.appendChild(label)
108 | })
109 | this.menu.appendChild(this.button)
110 | this.menu.appendChild(this.input)
111 | }
112 |
113 | createRanger() {
114 | let div = document.createElement('div')
115 | let input = document.createElement('input')
116 | let value = document.createElement('span')
117 | let background = document.createElement('i')
118 | div.className = 'range-box'
119 | input.type = 'range'
120 | input.value = 5
121 | input.min = 0
122 | input.max = 10
123 | div.appendChild(background)
124 | div.appendChild(input)
125 | div.appendChild(value)
126 | this.bindEvent(background, 'click', function() {
127 | if (this.valueType !== 3) return
128 | this.alpha.background = this.alpha.background === 'rgba(255,255,255,1)' ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)'
129 | })
130 | this.bindEvent(input, 'input', function(e) {
131 | if (this.valueType !== 3) return
132 | this.alpha.opacity = e.target.value / 10
133 | })
134 | return div
135 | }
136 |
137 | createLable () {
138 | let labels = []
139 | this.inputLabel.forEach(label => {
140 | let div = document.createElement('div')
141 | div.className = 'canvas-label'
142 | div.setAttribute('data-value', label.value)
143 | div.innerHTML = `
144 |
145 | ${label.text}
146 | `
147 | labels.push(div)
148 | })
149 | return labels
150 | }
151 |
152 | start(type) {
153 | this.startType = type
154 | this.imgGeted = false
155 | this.background.width = this.size.width
156 | this.background.height = this.size.height
157 | this.canvas.width = this.canvas.height = 2 * this.radius
158 | this.getScreenImage()
159 | }
160 |
161 | getScreenImage() {
162 | screenshots(this.src, error => {
163 | if(error)
164 | console.log("Screenshot failed", error);
165 | else
166 | this.image.src = this.src + '?' + Math.random()
167 | });
168 | }
169 |
170 | drawBackground() {
171 | this.bg.drawImage(this.image, 0, 0, this.size.width, this.size.height)
172 | this.imgData = this.bg.getImageData(0, 0, this.size.width, this.size.height)
173 | this.imgGeted = true
174 | this.view.appendChild(this.background)
175 | document.body.appendChild(this.menu)
176 | this.alpha.background = 'rgba(255,255,255,1)'
177 | this.alpha.opacity = 0.5
178 | }
179 |
180 | bindEvent(el, type, fn) {
181 | fn = fn.bind(this)
182 | el = typeof el === 'string' ? this[el] : el
183 | el.addEventListener(type, fn)
184 | }
185 |
186 | addEventListener() {
187 | this.bindEvent('image', 'load', this.drawBackground)
188 | this.bindEvent('background', 'mouseenter', this.showClip)
189 | this.bindEvent('menu', 'mouseenter', this.hideClip)
190 | this.bindEvent('button', 'click', this.menuToggle)
191 | this.bindEvent('canvas', 'mousedown', this.handleMousedown)
192 | this.labels.forEach(label => {
193 | this.bindEvent(label, 'click', this.changeType)
194 | })
195 | this.bindEvent(document.body, 'mousemove', this.drawEvent)
196 | }
197 |
198 | hideClip() {
199 | if (this.clipView.parentNode === this.view) {
200 | this.view.removeChild(this.clipView)
201 | }
202 | this.inMenu = true
203 | }
204 |
205 | showClip(e) {
206 | this.inMenu = false
207 | this.setPosition(e)
208 | this.getClipData()
209 | this.drawPoint()
210 | this.view.appendChild(this.clipView)
211 | }
212 |
213 | changeType(e) {
214 | let target = e.target
215 | while (target !== document.body) {
216 | if (target.dataset.value) {
217 | this.valueType = +target.dataset.value
218 | this.changeClass()
219 | break
220 | }
221 | target = target.parentNode
222 | }
223 | }
224 |
225 | changeClass() {
226 | document.querySelectorAll('.checked-value').forEach(dom => {
227 | dom.className = ''
228 | })
229 | this.menu.querySelector(`div[data-value="${this.valueType}"]`).firstElementChild.className = 'checked-value'
230 | if (this.valueType === 3) {
231 | this.input.classList.add('is-alpha')
232 | } else {
233 | this.input.classList.remove('is-alpha')
234 | }
235 | }
236 |
237 | menuToggle(e) {
238 | if (this.button.className === 'menu-show') {
239 | this.button.className = 'menu-hide'
240 | this.menu.classList.add('hide-view')
241 | } else {
242 | this.button.className = 'menu-show'
243 | this.menu.classList.remove('hide-view')
244 | }
245 | }
246 |
247 | drawEvent(e) {
248 | if (!this.imgGeted || this.inMenu) return
249 | this.setPosition(e)
250 | this.getClipData()
251 | this.drawPoint()
252 | }
253 |
254 | setPosition(e) {
255 | this.point = {
256 | x: e.clientX,
257 | y: e.clientY
258 | }
259 | this.current = {
260 | x: e.clientX - this.background.offsetLeft,
261 | y: e.clientY - this.background.offsetTop
262 | }
263 | }
264 |
265 | getClipData () {
266 | this.clipData = []
267 | let [x, y] = [~~(this.current.x - this.radius / this.range), ~~(this.current.y - this.radius / this.range)]
268 | let data = this.imgData.data
269 | let index, r, g, b, a
270 | if (!data) return
271 | for (let i = 0; i < this.clipRange; i ++) {
272 | for (let j = 0; j < this.clipRange; j ++) {
273 | index = ~~((y + i) * this.imgData.width + x + j)
274 | r = data[index * 4 + 0]
275 | g = data[index * 4 + 1]
276 | b = data[index * 4 + 2]
277 | a = data[index * 4 + 3] / 255
278 | this.clipData.push(`rgba(${r},${g},${b},${a})`)
279 | }
280 | }
281 | }
282 |
283 | drawPoint () {
284 | let ctx = this.ctx
285 | let resize = this.radius - this.range * this.clipRange / 2
286 | let length = this.clipData.length
287 | let current = ~~(length / 2)
288 | let x, y, cp = {}
289 | this.clipView.style.transform = `translate(${this.point.x}px, ${this.point.y}px)`
290 | ctx.save()
291 | for (let i = 0 ; i < length; i ++) {
292 | y = ~~(i / this.clipRange)
293 | x = i - y * this.clipRange
294 | if (i === current) {
295 | cp = {
296 | x: x * this.range + resize,
297 | y: y * this.range + resize
298 | }
299 | } else {
300 | ctx.lineWidth = 0.4
301 | ctx.strokeStyle = 'rgba(255,255,255,0.4)'
302 | ctx.fillStyle = this.clipData[i]
303 | ctx.fillRect(x * this.range + resize, y * this.range + resize, this.range, this.range)
304 | ctx.strokeRect(x * this.range + resize, y * this.range + resize, this.range, this.range)
305 | ctx.restore()
306 | }
307 | }
308 | ctx.lineWidth = 2
309 | ctx.strokeStyle = '#fc04db'
310 | ctx.fillStyle = this.clipData[current]
311 | ctx.fillRect(cp.x, cp.y, this.range, this.range)
312 | ctx.strokeRect(cp.x, cp.y, this.range, this.range)
313 | ctx.restore()
314 | this.setValue(this.clipData[current])
315 | }
316 | setValue(val) {
317 | let text = ''
318 | this.currentRGB = getRGB(val)
319 | if (this.valueType === 1) {
320 | text = '#'
321 | this.currentRGB.forEach(color => {
322 | let int = parseInt(color, 10).toString(16).toUpperCase()
323 | text += int.length === 1 ? `0${int}` : int
324 | })
325 | } else if (this.valueType === 2) {
326 | text = val
327 | } else if (this.valueType === 3) {
328 | let [r, g, b] = getRGB(val)
329 | let [r1, g1, b1] = getRGB(this.alpha.background)
330 | let r2, g2, b2, a2 = this.alpha.opacity
331 | r2 = colorFormat((r - r1 * (1 - a2)) / a2)
332 | g2 = colorFormat((g - g1 * (1 - a2)) / a2)
333 | b2 = colorFormat((b - b1 * (1 - a2)) / a2)
334 | text = `rgba(${r2},${g2},${b2},${a2})`
335 | }
336 | this.currentColor = text
337 | this.colorValue.innerText = text
338 | }
339 |
340 |
341 | handleMousedown(e) {
342 | if (e.button == 2 && this.valueType == 3) {
343 | this.alpha.background = `rgba(${this.currentRGB[0]},${this.currentRGB[1]},${this.currentRGB[2]},1)`
344 | }
345 | if (e.button == 0) {
346 | this.handleSend()
347 | }
348 | }
349 |
350 | handleSend () {
351 | mutation({
352 | type: 'HISTORY_COLOR',
353 | payload: this.currentColor
354 | })
355 | clipboard.writeText(this.currentColor)
356 | this.exitProject()
357 | setTimeout(()=>{
358 | ipcRenderer.send('close-pick-window', this.startType)
359 | ipcRenderer.send('show-notification')
360 | }, 30)
361 | }
362 |
363 | exitProject() {
364 | this.valueType = 1
365 | this.inMenu = false
366 | this.currentColor = ''
367 | this.changeClass()
368 | this.imgData = []
369 | this.clipData = []
370 | this.canvas.width = this.canvas.height = 0
371 | this.background.width = this.background.height = 0
372 | if (this.menu.parentNode === document.body) {
373 | document.body.removeChild(this.menu)
374 | }
375 | if (this.clipView.parentNode === this.view) {
376 | this.view.removeChild(this.clipView)
377 | }
378 | if (this.background.parentNode === this.view) {
379 | this.view.removeChild(this.background)
380 | }
381 | fs.unlinkSync(this.src)
382 | }
383 | }
384 | const app = new App({
385 | el: document.querySelector('.view')
386 | })
387 |
388 | ipcRenderer.on('start-point-pr', function(event, type) {
389 | app.start(type)
390 | })
391 |
--------------------------------------------------------------------------------