From e3838738dbd99f70d2dabbf1b587a0dc3fe9e02a Mon Sep 17 00:00:00 2001 From: muwoo <2424880409@qq.com> Date: Tue, 22 Jun 2021 15:35:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=88=AA=E5=B1=8F?= =?UTF-8?q?=EF=BC=8C=E4=B8=8B=E8=BD=BD=E6=96=B9=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/main/browsers/capture.js | 102 +++++ src/main/browsers/index.js | 1 + src/main/common/common.js | 4 - src/main/index.js | 2 + src/renderer/assets/api/config.js | 2 +- src/renderer/assets/common/constans.js | 16 +- src/renderer/assets/common/system.js | 5 + src/renderer/assets/common/utils.js | 19 +- src/renderer/store/modules/main.js | 9 +- .../plugins/capture/assets/audio/capture.mp3 | Bin 0 -> 33741 bytes .../plugins/capture/assets/iconfont/demo.css | 370 ++++++++++++++++ .../assets/iconfont/demo_fontclass.html | 64 +++ .../capture/assets/iconfont/demo_symbol.html | 95 ++++ .../capture/assets/iconfont/demo_unicode.html | 102 +++++ .../capture/assets/iconfont/iconfont.css | 25 ++ .../capture/assets/iconfont/iconfont.eot | Bin 0 -> 2252 bytes .../capture/assets/iconfont/iconfont.js | 1 + .../capture/assets/iconfont/iconfont.svg | 38 ++ .../capture/assets/iconfont/iconfont.ttf | Bin 0 -> 2084 bytes .../capture/assets/iconfont/iconfont.woff | Bin 0 -> 1424 bytes static/plugins/capture/capture-editor.js | 411 ++++++++++++++++++ static/plugins/capture/capture-renderer.js | 139 ++++++ static/plugins/capture/desktop-capturer.js | 17 + static/plugins/capture/index.html | 88 ++++ static/plugins/capture/logo.png | Bin 0 -> 3488 bytes static/plugins/capture/utils.js | 20 + 27 files changed, 1519 insertions(+), 12 deletions(-) create mode 100644 src/main/browsers/capture.js create mode 100644 static/plugins/capture/assets/audio/capture.mp3 create mode 100644 static/plugins/capture/assets/iconfont/demo.css create mode 100644 static/plugins/capture/assets/iconfont/demo_fontclass.html create mode 100644 static/plugins/capture/assets/iconfont/demo_symbol.html create mode 100644 static/plugins/capture/assets/iconfont/demo_unicode.html create mode 100644 static/plugins/capture/assets/iconfont/iconfont.css create mode 100644 static/plugins/capture/assets/iconfont/iconfont.eot create mode 100644 static/plugins/capture/assets/iconfont/iconfont.js create mode 100644 static/plugins/capture/assets/iconfont/iconfont.svg create mode 100644 static/plugins/capture/assets/iconfont/iconfont.ttf create mode 100644 static/plugins/capture/assets/iconfont/iconfont.woff create mode 100644 static/plugins/capture/capture-editor.js create mode 100644 static/plugins/capture/capture-renderer.js create mode 100644 static/plugins/capture/desktop-capturer.js create mode 100644 static/plugins/capture/index.html create mode 100644 static/plugins/capture/logo.png create mode 100644 static/plugins/capture/utils.js diff --git a/package.json b/package.json index 8b11e28..f85906b 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "dependencies": { "ant-design-vue": "^1.7.5", "axios": "^0.18.1", + "download": "^8.0.0", "download-git-repo": "^3.0.2", "electron-store": "^8.0.0", "marked": "^2.0.7", diff --git a/src/main/browsers/capture.js b/src/main/browsers/capture.js new file mode 100644 index 0000000..53885b8 --- /dev/null +++ b/src/main/browsers/capture.js @@ -0,0 +1,102 @@ +const { BrowserWindow, ipcMain, globalShortcut } = require("electron"); +const os = require('os') +const path = require('path') + +module.exports = () => { + let captureWins = [] + + let init = () => { + if (captureWins.length) { + return + } + createWindow(); + }; + + let createWindow = () => { + const { screen } = require('electron'); + let displays = screen.getAllDisplays(); + captureWins = displays.map((display) => { + let captureWin = new BrowserWindow({ + // window 使用 fullscreen, mac 设置为 undefined, 不可为 false + fullscreen: os.platform() === 'win32' || undefined, + width: display.bounds.width, + height: display.bounds.height, + x: display.bounds.x, + y: display.bounds.y, + transparent: true, + frame: false, + movable: false, + resizable: false, + enableLargerThanScreen: true, + hasShadow: false, + show: false, + webPreferences: { + enableRemoteModule: true, + nodeIntegration: true, + webSecurity: false, + // devTools: false, + } + }) + captureWin.setAlwaysOnTop(true, 'screen-saver') + captureWin.setVisibleOnAllWorkspaces(true) + captureWin.setFullScreenable(false) + + captureWin.loadFile(`${__static}/plugins/capture/index.html`); + + let { x, y } = screen.getCursorScreenPoint() + if (x >= display.bounds.x && x <= display.bounds.x + display.bounds.width && y >= display.bounds.y && y <= display.bounds.y + display.bounds.height) { + captureWin.focus() + } else { + captureWin.blur() + } + + captureWin.once('ready-to-show', () => captureWin.show()); + return captureWin + }); + }; + + let getWindow = () => captureWins; + + let useCapture = () => { + globalShortcut.register('Esc', () => { + if (captureWins) { + captureWins.forEach(win => win.close()) + captureWins = [] + } + }); + + globalShortcut.register('CmdOrCtrl+Shift+S', init) + + ipcMain.on('capture-screen', (e, { type = 'start', screenId, winId, x, y } = {}) => { + if (type === 'start') { + init() + } else if (type === 'complete') { + if (captureWins) { + captureWins.forEach(win => win.close()) + captureWins = [] + } + // nothing + } else if (type === 'select') { + captureWins.forEach(win => win.webContents.send('capture-screen', { type: 'select', screenId })) + } else if (type === 'getAllDisplays') { + const { screen } = require('electron'); + let displays = screen.getAllDisplays(); + const currentScreen = displays.filter(d => d.bounds.x === x && d.bounds.y === y)[0]; + e.sender.send('getAllDisplays', { + screen: { + scaleFactor: currentScreen.scaleFactor, + id: currentScreen.id, + bounds: currentScreen.bounds, + }, + winId, + }); + } + }); + } + + return { + init: init, + getWindow: getWindow, + useCapture, + }; +}; diff --git a/src/main/browsers/index.js b/src/main/browsers/index.js index 092a8a0..d2d059d 100644 --- a/src/main/browsers/index.js +++ b/src/main/browsers/index.js @@ -1,4 +1,5 @@ module.exports = () => ({ picker: require("./picker")(), separator: require("./separate")(), + capture: require("./capture")(), }); diff --git a/src/main/common/common.js b/src/main/common/common.js index 15a6dc3..edb72b2 100644 --- a/src/main/common/common.js +++ b/src/main/common/common.js @@ -45,10 +45,6 @@ export default function init(mainWindow) { }); ipcMain.on('init-shortcut', (event) => { - globalShortcut.register('Esc', () => { - mainWindow.show(); - event.sender.send('init-rubick'); - }); globalShortcut.register('ctrl+d', () => { event.sender.send('new-window'); }); diff --git a/src/main/index.js b/src/main/index.js index a29bd62..a1353bd 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -2,6 +2,7 @@ import { app, BrowserWindow, protocol } from 'electron' import '../renderer/store' import init from './common/common'; import createTray from './tray'; +const {capture} = require("./browsers")(); /** * Set `__static` path to static files in production * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html @@ -19,6 +20,7 @@ function createWindow () { /** * Initial window options */ + capture.useCapture() mainWindow = new BrowserWindow({ height: 60, useContentSize: true, diff --git a/src/renderer/assets/api/config.js b/src/renderer/assets/api/config.js index a0693f7..ae43039 100644 --- a/src/renderer/assets/api/config.js +++ b/src/renderer/assets/api/config.js @@ -1,4 +1,4 @@ export default { - development: 'http://localhost:7001', + development: 'http://rubick-server.qa.91jkys.com', production: 'http://rubick-server.qa.91jkys.com', }; diff --git a/src/renderer/assets/common/constans.js b/src/renderer/assets/common/constans.js index 598994e..2bcf52a 100644 --- a/src/renderer/assets/common/constans.js +++ b/src/renderer/assets/common/constans.js @@ -5,7 +5,7 @@ const PRE_ITEM_HEIGHT = 60; const SYSTEM_PLUGINS = [ { "pluginName": "rubick 帮助文档", - "logo": "logo.png", + "logo": "https://static.91jkys.com/activity/img/4eb6f2848b064f569c28fdf8495d5ec7.png", "features": [ { "code": "help", @@ -17,7 +17,7 @@ const SYSTEM_PLUGINS = [ }, { "pluginName": "屏幕颜色拾取", - "logo": "logo.png", + "logo": "https://static.91jkys.com/activity/img/6a1b4b8a17da45d680ea30b53a91aca8.png", "features": [ { "code": "pick", @@ -26,6 +26,18 @@ const SYSTEM_PLUGINS = [ }, ], "tag": 'rubick-color', + }, + { + "pluginName": "截屏", + "logo": "https://static.91jkys.com/activity/img/b34d30b426f24eb2b77bf434b8493495.png", + "features": [ + { + "code": "shortCut", + "explain": "rubick 屏幕截取", + "cmds": [ "截屏", "shortCut" ] + }, + ], + "tag": 'rubick-screen-short-cut', } ] diff --git a/src/renderer/assets/common/system.js b/src/renderer/assets/common/system.js index 3479aae..e276bd5 100644 --- a/src/renderer/assets/common/system.js +++ b/src/renderer/assets/common/system.js @@ -9,5 +9,10 @@ export default { pick() { ipcRenderer.send('start-picker') } + }, + 'rubick-screen-short-cut': { + shortCut() { + ipcRenderer.send('capture-screen', {type: 'start'}) + } } } diff --git a/src/renderer/assets/common/utils.js b/src/renderer/assets/common/utils.js index 7e87739..3a7155e 100644 --- a/src/renderer/assets/common/utils.js +++ b/src/renderer/assets/common/utils.js @@ -4,6 +4,7 @@ import path from 'path'; import fs from 'fs'; import process from 'child_process'; import Store from 'electron-store'; +import downloadFile from 'download'; const store = new Store(); @@ -44,6 +45,21 @@ function mkdirFolder(name) { }); } +async function downloadZip(downloadRepoUrl, name) { + const plugin_path = path.join(__static, './plugins'); + const targetUrl = downloadRepoUrl ? downloadRepoUrl : `https://github.com/clouDr-f2e/${name}/archive/refs/heads/master.zip`; + if (!(await existOrNot(plugin_path))) { + await mkdirFolder(plugin_path); + } + // 基础模版所在目录,如果是初始化,则是模板名称,否则是项目名称 + const temp_dest = `${plugin_path}/${name}-master`; + // 下载模板 + if (await existOrNot(temp_dest)) { + await process.execSync(`rm -rf ${temp_dest}`); + } + await downloadFile(targetUrl, `${__static}/plugins`,{extract: true}) +} + function downloadFunc(downloadRepoUrl, name) { const targetGit = downloadRepoUrl ? downloadRepoUrl : `github:clouDr-f2e/${name}`; const plugin_path = path.join(__static, './plugins'); @@ -82,7 +98,6 @@ const sysFile = { }, getUserPlugins() { try { - console.log(store.get('user-plugins').devPlugins) return store.get('user-plugins').devPlugins; } catch (e) { return [] @@ -92,6 +107,7 @@ const sysFile = { store.delete('user-plugins'); } } +sysFile.removeAllPlugins() function mergePlugins(plugins) { return [ @@ -137,4 +153,5 @@ export { sysFile, mergePlugins, find, + downloadZip, } diff --git a/src/renderer/store/modules/main.js b/src/renderer/store/modules/main.js index 860c7b4..8f18600 100644 --- a/src/renderer/store/modules/main.js +++ b/src/renderer/store/modules/main.js @@ -7,6 +7,7 @@ import { sysFile, mergePlugins, find, + downloadZip, } from '../../assets/common/utils'; import systemMethod from '../../assets/common/system'; import fs from "fs"; @@ -179,7 +180,7 @@ const actions = { ...cmds.map((cmd) => ({ name: cmd, value: 'plugin', - icon: 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`), + icon: plugin.sourceFile ? 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`) : plugin.logo, desc: fe.explain, type: plugin.type, click: (router) => { @@ -199,8 +200,8 @@ const actions = { }); }, async downloadPlugin({commit}, payload) { - await downloadFunc(payload.gitUrl, payload.name); - const fileUrl = find(path.join(__static, `plugins/${payload.name}`)); + await downloadZip(payload.gitUrl, payload.name); + const fileUrl = find(path.join(__static, `plugins/${payload.name}-master`)); // 复制文件 const config = JSON.parse(fs.readFileSync(`${fileUrl}/plugin.json`, 'utf-8')); const pluginConfig = { @@ -221,7 +222,7 @@ const actions = { icon: 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`), }, searchValue: '', - showMain: true, + showMain: true }); ipcRenderer.send('changeWindowSize-rubick', { height: getWindowHeight(), diff --git a/static/plugins/capture/assets/audio/capture.mp3 b/static/plugins/capture/assets/audio/capture.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2976e2f9f1e9c9ed693ec95de275b7d021f685d4 GIT binary patch literal 33741 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

NJv>r!;pYo}FcIQc`8+^56Ol*Nl>i6bh6lT=Qu4 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, 正淳 + */ + +/** 清除内外边距 **/ +body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ +dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ +pre, /* text formatting elements 文本格式元素 */ +form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */ +th, td /* table elements 表格元素 */ { + margin: 0; + padding: 0; +} + +/** 设置默认字体 **/ +body, +button, input, select, textarea /* for ie */ { + font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif; +} +h1, h2, h3, h4, h5, h6 { font-size: 100%; } +address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */ +code, kbd, pre, samp { font-family: courier new, courier, monospace; } /* 统一等宽字体 */ +small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */ + +/** 重置列表元素 **/ +ul, ol { list-style: none; } + +/** 重置文本格式元素 **/ +a { text-decoration: none; } +a:hover { text-decoration: underline; } + + +/** 重置表单元素 **/ +legend { color: #000; } /* for ie6 */ +fieldset, img { border: 0; } /* img 搭车:让链接里的 img 无边框 */ +button, input, select, textarea { font-size: 100%; } /* 使得表单元素在 ie 下能继承字体大小 */ +/* 注:optgroup 无法扶正 */ + +/** 重置表格元素 **/ +table { border-collapse: collapse; border-spacing: 0; } + +/* 清除浮动 */ +.ks-clear:after, .clear:after { + content: '\20'; + display: block; + height: 0; + clear: both; +} +.ks-clear, .clear { + *zoom: 1; +} + +.main { + padding: 30px 100px; +width: 960px; +margin: 0 auto; +} +.main h1{font-size:36px; color:#333; text-align:left;margin-bottom:30px; border-bottom: 1px solid #eee;} + +.helps{margin-top:40px;} +.helps pre{ + padding:20px; + margin:10px 0; + border:solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists{ + width: 100% !important; + +} + +.icon_lists li{ + float:left; + width: 100px; + height:180px; + text-align: center; + list-style: none !important; +} +.icon_lists .icon{ + font-size: 42px; + line-height: 100px; + margin: 10px 0; + color:#333; + -webkit-transition: font-size 0.25s ease-out 0s; + -moz-transition: font-size 0.25s ease-out 0s; + transition: font-size 0.25s ease-out 0s; + +} +.icon_lists .icon:hover{ + font-size: 100px; +} + + + +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p, +.markdown pre { + margin: 1em 0; +} + +.markdown > p, +.markdown > blockquote, +.markdown > .highlight, +.markdown > ol, +.markdown > ul { + width: 80%; +} + +.markdown ul > li { + list-style: circle; +} + +.markdown > ul li, +.markdown blockquote ul > li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown > ul li p, +.markdown > ol li p { + margin: 0.6em 0; +} + +.markdown ol > li { + list-style: decimal; +} + +.markdown > ol li, +.markdown blockquote ol > li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown pre { + border-radius: 6px; + background: #f7f7f7; + padding: 20px; +} + +.markdown pre code { + border: none; + background: #f7f7f7; + margin: 0; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown > table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown > table th { + white-space: nowrap; + color: #333; + font-weight: 600; + +} + +.markdown > table th, +.markdown > table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown > table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; + font-style: italic; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown > br, +.markdown > p > br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +pre{ + background: #fff; +} + + + + + diff --git a/static/plugins/capture/assets/iconfont/demo_fontclass.html b/static/plugins/capture/assets/iconfont/demo_fontclass.html new file mode 100644 index 0000000..09f8816 --- /dev/null +++ b/static/plugins/capture/assets/iconfont/demo_fontclass.html @@ -0,0 +1,64 @@ + + + + + + IconFont + + + + +

+

IconFont 图标

+
    + +
  • + +
    下载
    +
    .icon-xiazai
    +
  • + +
  • + +
    关闭
    +
    .icon-guanbi
    +
  • + +
  • + +
    重置
    +
    .icon-zhongzhi
    +
  • + +
  • + +
    对号
    +
    .icon-duihao
    +
  • + +
+ +

font-class引用

+
+ +

font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题。

+

与unicode使用方式相比,具有如下特点:

+
    +
  • 兼容性良好,支持ie8+,及所有现代浏览器。
  • +
  • 相比于unicode语意明确,书写更直观。可以很容易分辨这个icon是什么。
  • +
  • 因为使用class来定义图标,所以当要替换图标时,只需要修改class里面的unicode引用。
  • +
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的fontclass代码:

+ + +
<link rel="stylesheet" type="text/css" href="./iconfont.css">
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<i class="iconfont icon-xxx"></i>
+
+

"iconfont"是你项目下的font-family。可以通过编辑项目查看,默认是"iconfont"。

+
+
+ + diff --git a/static/plugins/capture/assets/iconfont/demo_symbol.html b/static/plugins/capture/assets/iconfont/demo_symbol.html new file mode 100644 index 0000000..8154297 --- /dev/null +++ b/static/plugins/capture/assets/iconfont/demo_symbol.html @@ -0,0 +1,95 @@ + + + + + + IconFont + + + + + + +
+

IconFont 图标

+
    + +
  • + +
    下载
    +
    #icon-xiazai
    +
  • + +
  • + +
    关闭
    +
    #icon-guanbi
    +
  • + +
  • + +
    重置
    +
    #icon-zhongzhi
    +
  • + +
  • + +
    对号
    +
    #icon-duihao
    +
  • + +
+ + +

symbol引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个svg的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过font-size,color来调整样式。
  • +
  • 兼容性较差,支持 ie9+,及现代浏览器。
  • +
  • 浏览器渲染svg的性能一般,还不如png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的symbol代码:

+
<script src="./iconfont.js"></script>
+

第二步:加入通用css代码(引入一次就行):

+
<style type="text/css">
+.icon {
+   width: 1em; height: 1em;
+   vertical-align: -0.15em;
+   fill: currentColor;
+   overflow: hidden;
+}
+</style>
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+        
+
+ + diff --git a/static/plugins/capture/assets/iconfont/demo_unicode.html b/static/plugins/capture/assets/iconfont/demo_unicode.html new file mode 100644 index 0000000..1a81d12 --- /dev/null +++ b/static/plugins/capture/assets/iconfont/demo_unicode.html @@ -0,0 +1,102 @@ + + + + + + IconFont + + + + + +
+

IconFont 图标

+
    + +
  • + +
    下载
    +
    &#xe627;
    +
  • + +
  • + +
    关闭
    +
    &#xe66c;
    +
  • + +
  • + +
    重置
    +
    &#xe633;
    +
  • + +
  • + +
    对号
    +
    &#xeeda;
    +
  • + +
+

unicode引用

+
+ +

unicode是字体在网页端最原始的应用方式,特点是:

+
    +
  • 兼容性最好,支持ie6+,及所有现代浏览器。
  • +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • +
+
+

注意:新版iconfont支持多色图标,这些多色图标在unicode模式下将不能使用,如果有需求建议使用symbol的引用方式

+
+

unicode使用步骤如下:

+

第一步:拷贝项目下面生成的font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.eot');
+  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
+  url('iconfont.woff') format('woff'),
+  url('iconfont.ttf') format('truetype'),
+  url('iconfont.svg#iconfont') format('svg');
+}
+
+

第二步:定义使用iconfont的样式

+
.iconfont{
+  font-family:"iconfont" !important;
+  font-size:16px;font-style:normal;
+  -webkit-font-smoothing: antialiased;
+  -webkit-text-stroke-width: 0.2px;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
<i class="iconfont">&#x33;</i>
+ +
+

"iconfont"是你项目下的font-family。可以通过编辑项目查看,默认是"iconfont"。

+
+
+ + + + diff --git a/static/plugins/capture/assets/iconfont/iconfont.css b/static/plugins/capture/assets/iconfont/iconfont.css new file mode 100644 index 0000000..7034fb1 --- /dev/null +++ b/static/plugins/capture/assets/iconfont/iconfont.css @@ -0,0 +1,25 @@ + +@font-face {font-family: "iconfont"; + src: url('iconfont.eot?t=1538684099545'); /* IE9*/ + src: url('iconfont.eot?t=1538684099545#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAWQAAsAAAAACCQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8nVDMY21hcAAAAYAAAABrAAABsu3anlxnbHlmAAAB7AAAAZYAAAG869Cas2hlYWQAAAOEAAAALgAAADYS2G+AaGhlYQAAA7QAAAAcAAAAJAfeA4ZobXR4AAAD0AAAAA4AAAAUFAAAAGxvY2EAAAPgAAAADAAAAAwBEAFgbWF4cAAAA+wAAAAfAAAAIAESAENuYW1lAAAEDAAAAUUAAAJtPlT+fXBvc3QAAAVUAAAAPAAAAE0pDHeHeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeqb+7xdzwv4EhhrmBoQEozAiSAwDxJgzseJztkbsNgDAMRF8+IBSxBw0dC2SgVIzLAC5ZIdgxBUNw0bPsU+LiAkxAUnYlQzgJmJq6YfiJMvxM1bmwEImyySHtvnqHb/8q6D0/1id9Ybtnfq2j1nfKlp5jacvh2I9IczQ77sshP1MOG7MAeJwdj0FrE1EUhe95z8zLvOnM9GVC0pSZmEybmSKYTdsZRIhKXRgQLfgTpIiLoJtCdZFqf4HQnRsLhazabNxamD/gyoIb/QkK3ehqUu/4Npdzz8c795AkfvKSR0gRZUSIrQgtS/WtJN/eSirVRbs/Qr49RO0eMt75MJWjTJrEymreZEJ+9hq4vVV+vLOBRrgkHmXiJBujZhbj1XM76Oi3JjJ444SW1ufMbxp/kuZC3t2Y+Mb4x8xinB375p2a6U5gT1BU+8X9iW3rjp5Jj0j8v/WpmJNNAVGA1ggpEg8DWF3kyIb4IR7qSOnywgnr4kn5RddDR+xoFYnD8oKHZr8errLBlNhhir8E0XUpL8Uv6rEYQnlQa+18jTtzdRZxknJKq80hI8jTzW/T6VnjtCg+NWf7R3Mp50fZ81t6ZVnovb8vXzzA9f6BH5xNi+L1h8pkZD1Worli71qPd39XedzjvTihZaJBnA6SEdo1ZF0oWApftb347tZ6HgwOdezqxav60jN15fRd5yf+uDd6bnmlBf0D1bBUzQAAeJxjYGRgYADi+Y6Xrsbz23xl4GZhAIHrd6YeRtD//7IwMIO4HAxMIAoAaF8MWwAAeJxjYGRgYG7438AQw8IAAkCSkQEVsAIARwsCbnicY2FgYGBBwwABBAAVAAAAAAAAAFYAggC6AN54nGNgZGBgYGUwZ2BmAAEmIOYCQgaG/2A+AwAOdQFWAHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgJWRiZGZkYWRlZGNga0iM7EqMZMtvTQxLymToyojPy+9KiOTLaU0MyMxn4EBAMN8Cyw=') format('woff'), + url('iconfont.ttf?t=1538684099545') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('iconfont.svg?t=1538684099545#iconfont') format('svg'); /* iOS 4.1- */ +} + +.iconfont { + font-family:"iconfont" !important; + font-size:16px; + font-style:normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-xiazai:before { content: "\e627"; } + +.icon-guanbi:before { content: "\e66c"; } + +.icon-zhongzhi:before { content: "\e633"; } + +.icon-duihao:before { content: "\eeda"; } + diff --git a/static/plugins/capture/assets/iconfont/iconfont.eot b/static/plugins/capture/assets/iconfont/iconfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..1e710664ff6b4434b63d79c4748a19230cf7e75b GIT binary patch literal 2252 zcmd^B-)j_C6h3EWcV_o)lFcSDQAyG<#$9XeMibq%R$?^PRIoLu&6CiMo5}9V&d##g z*d(;N7249L3ep#8RS={PYD;}9N&|gWC{(ZnmHM(2Bmp06pEl`tXJ)ac{s+Cwx%WHY zIrqmo_hwG+got_}ObW@SP*8ONm~$-Ap)pT?-n%r^XOH(@dd}8SGvz2v4i#w9?aMGU z(GjvJL(`NelS*LPX&g+6rr<4o4cbmg+6K$Z=7W}-MyNt6Z5uuM{=OfUZ!Us-1MI%T zNBjDBe>-*u`p3}6)3#Y$T)21~`k#;=Gx^!cC%;~}Nu+)U{Z`g8Ct7}YD)9XSlFEY6 zAE}=M--7gJ?ea_;0DSMEYs))nlbiWN%>M>@gKf?fxrIhxUklwRn6@=^Z21$S-~=*g z7oDl{s|_E0L8Qz1@5-~Qm6Zw|4?rbCUr(W{qYJtW*h_mKKl>oM`x!+%-}?`~{zW=@ z<4P@n1fYfd>+)6#(x6AXtRMl873qd1cM{Z1TZ#7{ICKm%y@fps=W`hooUg$qtqg`q z^I7Nt;&V7-NcmDhmgy^RxYC3!bORbwbHVpCL&Lju-a0*67e2gYkLJA>dmGWetk#n!ke49rL#eK zC#76Y9pqqa`QZBN;nsCxF4h&}*+{1*#C7-&#G-a$P*LC7Y)50U=y~|^!PNO^tfF5P z>srG$&r5!J*baxqI&oEPk`Gs&^{{e-!qiIC%I!m(;CK^nVy&A8IhEuG%6`$Mi`wl- zXGnRkc1wghBg%--yOcAv+ZY7wq0aR%z)Kl{7nVfcR%+@!WswXhN!FWK-`Fv@5iuil zC_V86c4_axCPS=V8Tjq=>1!=l=I1Z9U7bFALsf5_O{KSpwatpK{?29g^2+o~wDsEQ z`T62GX@pB}kFK<>4UcJu#+KxMh~rb`GCrQPsVA{1KExeCPIa@+n$CAcc=>)~&}iZq zpAkKcV%Z5b9MzvjHZ(>a@?VVsqp|i>xXEb24Z5pg!8H&M%kRM&A1kEeUOip~-|OQ5 zFupPJB=`%C&<-DK;P?1g&}%g6;|MZd!fW9`7&EreXFg`c@V$=}6po*Ltb)Jm;{fnO z9|uuN7JaNyGpjxpG|sI)j!>L)Vx(l5WoyDXF>B<~PGQn1ltmpJu`<(nvs72>uRr2oT1>>;nnAvqEo2X{WxVA zb=-iIP^WF61AbMvP|Nec1{Gb~NvxBGcapO1tRn5AKK$zc1=RhM?tcfid?)Uc!zk<3 r%UX~XRyn{ye5bXUoH=Law9K?wIFSp@Wt~E1E}PRPrgK@-acBPn3(rne literal 0 HcmV?d00001 diff --git a/static/plugins/capture/assets/iconfont/iconfont.js b/static/plugins/capture/assets/iconfont/iconfont.js new file mode 100644 index 0000000..4d8c980 --- /dev/null +++ b/static/plugins/capture/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +(function(window){var svgSprite='';var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file diff --git a/static/plugins/capture/assets/iconfont/iconfont.svg b/static/plugins/capture/assets/iconfont/iconfont.svg new file mode 100644 index 0000000..cfebe6b --- /dev/null +++ b/static/plugins/capture/assets/iconfont/iconfont.svg @@ -0,0 +1,38 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/plugins/capture/assets/iconfont/iconfont.ttf b/static/plugins/capture/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7d18bc09c3af12539d643cd1fd9290f721debfdf GIT binary patch literal 2084 zcmd^AQEwDg6h3EWcV_o)+wHcc1=7}`z%C+gp@pp?DTQJy2}%RlClZ}*r@Kpbb~f8B zwvEC{qQ)mlh%ZEigoOA2)ySKH*u+;!jMiWpAU9>!5yLM-Vh+hGF^yKN@zC&M*{RI1c*yCy2EUqknbr$xY znBQgcb5l=#zIum9{TlY&tYuEN{O(ks{R1E`)8!W%-ur||m+@~B&aPJ0D|9vh zlL${)&9fuuGGMPA`Q^8FqKE#Xh^Kw<@be#~&|BAQ0W<(3M2cK6WTqeuc(lt365!UQ z6GQq1b<-Z=VQ^gNfyA#~ZnbfO3S!E9dptLne0?6QjNF426@m}Y5NzRg zL#ZaJ{nh;H@;WMi7DFKiL+AY@R&i)-&9cV-`)F_*FyyxZU8yF#DZ5?m_olp&YTldj zEn8jorh9=hbet~H_w-omaD7WyI;o3N=y8wM#qFBDO^Xj4*dLcxH+O6sk4ySSyZL3tykTu&Y6U~KL9*4yFMEn+^_72~-` zrzXU0==)<)J29ZBukEs@VtsqZz;@J((qZ((6WFD_1Dg!8dZYi>3m0y++*n+^ z)^>B|(t@fkTuP;Pi_Oi7u>Q$qj_~@-Y_#>(g~i3|k!_8UNBnnVz-X*J z6>c+HaD#3(B;17QVfj7S+9^yqg|euFQ&wgsZbSL>`U&gQ=?YFD%JB$pU?D_%TvZw z$+3+QB((B*$0(MZb5^>X%$Cc=gT1|SBgrSzj*U;yFv_;bq%xk9WY7f7;hCc}IaHu2 z_gRK+BXbJ+49!p;v8Cs{8`a}bmuMO~nbn|ul%&1SMU6sLaFrX+Gfn5QTOUqY#+eMv z5;EC9BmN03oForyP|@|BL!LC$Ny@rWMLI~m_|+?_>yvJ!wtP?AR}QQEfAwD@$O@|* l;2^%!+HB68H*;EM#w<+aLi1UtkeScsw8@!V)^yzHe*suEJIDY4 literal 0 HcmV?d00001 diff --git a/static/plugins/capture/assets/iconfont/iconfont.woff b/static/plugins/capture/assets/iconfont/iconfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..5d9f432a7222d3bc18be8fad2504c41bc5f3a4b2 GIT binary patch literal 1424 zcmY*X3pCVO9KQdV7&fWS zmQ@~YO(7zw!AUAqLX@|Zs8M0BZO@*wzjMBOf8Te$d+zVv^ToJ3H~<8|lF9+{tGkTu zH@@2czZ}V4BmfW@sG32Ls+MbK+{pw}sO^E=4#Im+iAM)Lj2a0wF65zbubFvX>K}B3 z!-D-ZL_S3QR98`LFq6sv0Gk4RJ0L8S`y#l(P=wkw$hSb~N)KSug2ULcQ0szR0YXIu zE=MBhtG*%F;#PquB4`*j7T$}6cYv)Bnus#64X1`N0ayb)h&=$9Fe}gH-y$QT*zmoc z(6c(Xs{t-1Jyu9Z87MkL%b|BsQTEXfveY3IrE)CTe}>@*&)Y%9s^Hp`gy8bwhW%49XkVY>avYFkP#HP*HEziZ@a zrQPI}A^IIMxmfUC4>uhvoDtNm;~L}aC{~&()za%U{zFK`IQnrzgYNyG= z!x)#n2Y%URyGyp9=oVGNH~s4J^k$VJzvh9~%a}oQjcl9H4f|D6XE_f$+UqAe=9#4KQ-uXC#f91ht}ni` z%h(s7rf4MDa6aS2* z%F#`j%XM^8V%lX?${78;P^mjOu+Q+ReVsMwwx!(+-gIEm_;k5T(Oq%K0E3k9akHqw zE1|xY=OEI>GlzB6%UK&oo<^HOZ>2@tWxJ}r>~6*mkRP+k^S5r83X0@Sf7B`_tPX=_(`XUxn|j^Vdq8r zUR%fMU{!w0Tv9r#5r3uT&Z$1?@y0ceq74YGgvn|(62~KSCO(rG6}|Cj?y-Fpomx8g zA9qGPp70&Hrf;<~=!;{N!I-|>b<0%cFr!ZL5G(DlUdLb{m2u=GB6lQw%O zJ0|fUr+=hjR~hr1jZh(1>G@7ZfWD6$<*x?G+nzW)P9b`FvbExKw^{U?`GB$Q%c39S z!Z{bDmatE&JUhX+9HA%}Ud%dk<=%%E@ysC>74W8FF5(@!R#s|^jgv)Na4wp1Aee&l z`_CUP8EjH)X$F|RxdKGB3IiPFG2t+iRG2-R7J#e)sxaNF_3t zcn&3{IC#WMWWVIv*JgIHIgCx^B=?GFA5&te>owwKdLuL4U+rEXyjr{HC*l*o%H8OC zKiOIb6P--oxCo-!8%b~-E9 zkOF(Idy*y*3+BNrr8xgbd~5w6rxhT3DEvPB?$v*$alwwY)7;YnzMxQ$DmX33 { + currentScreen = await getCurrentScreen(); + this.scaleFactor = currentScreen.scaleFactor + this.screenWidth = currentScreen.bounds.width + this.screenHeight = currentScreen.bounds.height + this.init().then(() => { + // console.log('init') + }) + })(); + this.$bg = $bg + this.ctx = $canvas.getContext('2d') + + this.onMouseDown = this.onMouseDown.bind(this) + this.onMouseMove = this.onMouseMove.bind(this) + this.onMouseUp = this.onMouseUp.bind(this) + } + + async init() { + this.$bg.style.backgroundImage = `url(${this.imageSrc})` + this.$bg.style.backgroundSize = `${this.screenWidth}px ${this.screenHeight}px` + let canvas = document.createElement('canvas') + let ctx = canvas.getContext('2d') + let img = await new Promise((resolve) => { + let img = new Image() + img.src = this.imageSrc + if (img.complete) { + resolve(img) + } else { + img.onload = () => resolve(img) + } + }) + + canvas.width = img.width + canvas.height = img.height + ctx.drawImage(img, 0, 0) + this.bgCtx = ctx + + document.addEventListener('mousedown', this.onMouseDown) + document.addEventListener('mousemove', this.onMouseMove) + document.addEventListener('mouseup', this.onMouseUp) + } + + onMouseDown(e) { + if (this.disabled) { + return + } + this.mouseDown = true + const { pageX, pageY } = e + if (this.selectRect) { + const { + w, h, x, y, r, b, + } = this.selectRect + if (this.selectAnchorIndex !== -1) { + this.startPoint = { + x: pageX, + y: pageY, + moved: false, + selectRect: { + w, h, x, y, r, b, + }, + rawRect: { + w, h, x, y, r, b, + }, + } + this.action = RESIZE + return + } + this.startPoint = { + x: e.pageX, + y: e.pageY, + moved: false, + } + if (pageX > x && pageX < r && pageY > y && pageY < b) { + this.action = MOVING_RECT + this.startDragRect = { + x: pageX, + y: pageY, + selectRect: { + x, y, w, h, r, b, + }, + } + } else { + this.action = CREATE_RECT + } + } else { + this.action = CREATE_RECT + this.startPoint = { + x: e.pageX, + y: e.pageY, + moved: false, + } + e.stopPropagation() + e.preventDefault() + } + } + + onMouseDrag(e) { + if (this.disabled) { + return + } + e.stopPropagation() + e.preventDefault() + + const { pageX, pageY } = e + let startDragging + let selectRect = this.selectRect + if (!this.startPoint.moved) { + if (Math.abs(this.startPoint.x - pageX) > 10 || Math.abs(this.startPoint.y - pageY) > 10) { + this.startPoint.moved = true + startDragging = true + } + } + if (!this.startPoint.moved) { + return + } + + if (this.action === MOVING_RECT) { + // 移动选区 + if (startDragging) { + this.emit('start-dragging', selectRect) + } + this.emit('dragging', selectRect) + const { w, h } = selectRect + const { x: startX, y: startY } = this.startPoint + let newX = this.startDragRect.selectRect.x + pageX - startX + let newY = this.startDragRect.selectRect.y + pageY - startY + let newR = newX + w + let newB = newY + h + if (newX < 0) { + newX = 0 + newR = w + } else if (newR > this.screenWidth) { + newR = this.screenWidth + newX = newR - w + } + if (newY < 0) { + newY = 0 + newB = h + } else if (newB > this.screenHeight) { + newB = this.screenHeight + newY = newB - h + } + this.selectRect = { + w, + h, + x: newX, + y: newY, + r: newR, + b: newB, + } + this.drawRect() + } else if (this.action === RESIZE) { + this.emit('dragging', selectRect) + let { row, col } = ANCHORS[this.selectAnchorIndex] + if (row) { + this.startPoint.rawRect[row] = this.startPoint.selectRect[row] + pageX - this.startPoint.x + selectRect.x = this.startPoint.rawRect.x + selectRect.r = this.startPoint.rawRect.r + if (selectRect.x > selectRect.r) { + let x = selectRect.r + selectRect.r = selectRect.x + selectRect.x = x + } + selectRect.w = selectRect.r - selectRect.x + this.startPoint.rawRect.w = selectRect.w + } + if (col) { + this.startPoint.rawRect[col] = this.startPoint.selectRect[col] + pageY - this.startPoint.y + selectRect.y = this.startPoint.rawRect.y + selectRect.b = this.startPoint.rawRect.b + + if (selectRect.y > selectRect.b) { + let y = selectRect.b + selectRect.b = selectRect.y + selectRect.y = y + } + selectRect.h = selectRect.b - selectRect.y + this.startPoint.rawRect.h = selectRect.h + } + this.drawRect() + } else { + // 生成选区 + const { pageX, pageY } = e + let x, y, w, h, r, b + if (this.startPoint.x > pageX) { + x = pageX + r = this.startPoint.x + } else { + r = pageX + x = this.startPoint.x + } + if (this.startPoint.y > pageY) { + y = pageY + b = this.startPoint.y + } else { + b = pageY + y = this.startPoint.y + } + w = r - x + h = b - y + + + this.selectRect = { + x, y, w, h, r, b, + } + selectRect = this.selectRect + if (startDragging) { + this.emit('start-dragging', selectRect) + } + this.emit('dragging', selectRect) + this.drawRect(x, y, w, h) + } + + + } + + drawRect() { + if (this.disabled) { + return + } + if (!this.selectRect) { + this.$canvas.style.display = 'none' + return + } + const { + x, y, w, h, + } = this.selectRect + + const scaleFactor = this.scaleFactor + let margin = 7 + let radius = 5 + this.$canvas.style.left = `${x - margin}px` + this.$canvas.style.top = `${y - margin}px` + this.$canvas.style.width = `${w + margin * 2}px` + this.$canvas.style.height = `${h + margin * 2}px` + this.$canvas.style.display = 'block' + this.$canvas.width = (w + margin * 2) * scaleFactor + this.$canvas.height = (h + margin * 2) * scaleFactor + + if (w && h) { + let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor) + this.ctx.putImageData(imageData, margin * scaleFactor, margin * scaleFactor) + } + this.ctx.fillStyle = '#ffffff' + this.ctx.strokeStyle = '#67bade' + this.ctx.lineWidth = 2 * this.scaleFactor + + this.ctx.strokeRect(margin * scaleFactor, margin * scaleFactor, w * scaleFactor, h * scaleFactor) + this.drawAnchors(w, h, margin, scaleFactor, radius) + } + + drawAnchors(w, h, margin, scaleFactor, radius) { + if (this.disabled) { + return + } + if (this.mouseDown && this.action === CREATE_RECT) { + this.anchors = null + return + } + this.ctx.beginPath() + let anchors = [ + [0, 0], + [w * this.scaleFactor / 2, 0], + [w * this.scaleFactor, 0], + + [0, h * this.scaleFactor / 2], + [w * this.scaleFactor, h * this.scaleFactor / 2], + + [0, h * this.scaleFactor], + [w * this.scaleFactor / 2, h * this.scaleFactor], + [w * this.scaleFactor, h * this.scaleFactor], + ] + this.anchors = anchors.map(([x, y]) => [this.selectRect.x + x / scaleFactor, this.selectRect.y + y / scaleFactor]) + anchors.forEach(([x, y], i) => { + this.ctx.arc(x + margin * scaleFactor, y + margin * scaleFactor, radius * scaleFactor, 0, 2 * Math.PI) + let next = anchors[(i + 1) % anchors.length] + this.ctx.moveTo(next[0] + margin * scaleFactor + radius * scaleFactor, next[1] + margin * scaleFactor) + }) + this.ctx.closePath() + this.ctx.fill() + this.ctx.stroke() + } + + onMouseMove(e) { + if (this.disabled) { + return + } + if (this.mouseDown) { + this.onMouseDrag(e) + return + } + this.selectAnchorIndex = -1 + if (this.selectRect) { + const { pageX, pageY } = e + const { + x, y, r, b, + } = this.selectRect + let selectAnchor, selectIndex = -1 + if (this.anchors) { + this.anchors.forEach(([x, y], i) => { + if (Math.abs(pageX - x) <= 10 && Math.abs(pageY - y) <= 10) { + selectAnchor = [x, y] + selectIndex = i + } + }) + } + if (selectAnchor) { + this.selectAnchorIndex = selectIndex + document.body.style.cursor = ANCHORS[selectIndex].cursor + this.emit('moving') + return + } + if (pageX > x && pageX < r && pageY > y && pageY < b) { + document.body.style.cursor = 'move' + } else { + document.body.style.cursor = 'auto' + } + this.emit('moving') + } + } + + onMouseUp(e) { + if (this.disabled) { + return + } + if (!this.mouseDown) { + return + } + this.mouseDown = false + e.stopPropagation() + e.preventDefault() + this.emit('mouse-up') + if (!this.startPoint.moved) { + this.emit('end-moving') + return + } + this.emit('end-dragging') + this.drawRect() + this.startPoint = null + } + + getImageUrl() { + const scaleFactor = this.scaleFactor + const { + x, y, w, h, + } = this.selectRect + if (w && h) { + let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor) + let canvas = document.createElement('canvas') + canvas.width = w + canvas.height = h + let ctx = canvas.getContext('2d') + ctx.putImageData(imageData, 0, 0) + return canvas.toDataURL() + } + return '' + } + + disable() { + this.disabled = true + } + + enable() { + this.disabled = false + } + + reset() { + this.anchors = null + this.startPoint = null + this.selectRect = null + this.startDragRect = null + this.selectAnchorIndex = -1 + this.drawRect() + this.emit('reset') + } +} + + +exports.CaptureEditor = CaptureEditor +exports.CREATE_RECT = CREATE_RECT +exports.MOVING_RECT = MOVING_RECT +exports.RESIZE = RESIZE diff --git a/static/plugins/capture/capture-renderer.js b/static/plugins/capture/capture-renderer.js new file mode 100644 index 0000000..7766129 --- /dev/null +++ b/static/plugins/capture/capture-renderer.js @@ -0,0 +1,139 @@ +const {ipcRenderer, clipboard, nativeImage, remote} = require('electron') + +const fs = require('fs') +const { getScreenSources } = require('./desktop-capturer') +const { CaptureEditor } = require('./capture-editor') +const { getCurrentScreen } = require('./utils') + +const $canvas = document.getElementById('js-canvas') +const $bg = document.getElementById('js-bg') +const $sizeInfo = document.getElementById('js-size-info') +const $toolbar = document.getElementById('js-toolbar') +const $jsMask= document.getElementById('js-mask') + +const $btnClose = document.getElementById('js-tool-close') +const $btnOk = document.getElementById('js-tool-ok') +const $btnSave = document.getElementById('js-tool-save') +const $btnReset = document.getElementById('js-tool-reset') + +let currentScreen; + +(async () => { + currentScreen = await getCurrentScreen(); +})(); +// 右键取消截屏 +document.body.addEventListener('mousedown', (e) => { + if (e.button === 2) { + window.close() + } +}, true) + +// console.time('capture') +getScreenSources({}, (imgSrc) => { + $jsMask.style.display = 'block'; + let capture = new CaptureEditor($canvas, $bg, imgSrc) + + let onDrag = (selectRect) => { + $toolbar.style.display = 'none' + $sizeInfo.style.display = 'block' + $sizeInfo.innerText = `${selectRect.w} * ${selectRect.h}` + if (selectRect.y > 35) { + $sizeInfo.style.top = `${selectRect.y - 30}px` + } else { + $sizeInfo.style.top = `${selectRect.y + 10}px` + } + $sizeInfo.style.left = `${selectRect.x}px` + } + capture.on('start-dragging', onDrag) + capture.on('dragging', onDrag) + + let onDragEnd = () => { + if (capture.selectRect) { + ipcRenderer.send('capture-screen', { + type: 'select', + screenId: currentScreen.id, + }) + const { + r, b, + } = capture.selectRect + $toolbar.style.display = 'flex' + $toolbar.style.top = `${b + 15}px` + $toolbar.style.right = `${window.screen.width - r}px` + } + } + capture.on('end-dragging', onDragEnd) + + ipcRenderer.on('capture-screen', (e, { type, screenId }) => { + if (type === 'select') { + if (screenId && screenId !== currentScreen.id) { + capture.disable() + } + } + }) + + capture.on('reset', () => { + $toolbar.style.display = 'none' + $sizeInfo.style.display = 'none' + }) + + $btnClose.addEventListener('click', () => { + ipcRenderer.send('capture-screen', { + type: 'complete', + }) + window.close() + }) + + $btnReset.addEventListener('click', () => { + capture.reset() + }) + + let selectCapture = () => { + if (!capture.selectRect) { + return + } + let url = capture.getImageUrl() + remote.getCurrentWindow().hide() + clipboard.writeImage(nativeImage.createFromDataURL(url)) + ipcRenderer.send('capture-screen', { + type: 'complete', + url, + }) + + } + $btnOk.addEventListener('click', selectCapture) + + $btnSave.addEventListener('click', () => { + let url = capture.getImageUrl() + + remote.getCurrentWindow().hide() + remote.dialog.showSaveDialog({ + filters: [{ + name: 'Images', + extensions: ['png', 'jpg', 'gif'], + }], + }).then(({filePath}) => { + if (filePath) { + fs.writeFile(filePath, new Buffer(url.replace('data:image/png;base64,', ''), 'base64'), () => { + ipcRenderer.send('capture-screen', { + type: 'complete' + }) + window.close() + }) + } else { + ipcRenderer.send('capture-screen', { + type: 'complete' + }) + window.close() + } + }) + }) + + window.addEventListener('keypress', (e) => { + if (e.code === 'Enter') { + selectCapture() + } + }) +}) + + + diff --git a/static/plugins/capture/desktop-capturer.js b/static/plugins/capture/desktop-capturer.js new file mode 100644 index 0000000..55ff665 --- /dev/null +++ b/static/plugins/capture/desktop-capturer.js @@ -0,0 +1,17 @@ +const {desktopCapturer} = require('electron'); + +exports.getScreenSources = async ({ types = ['screen'] } = {}, callback) => { + let curScreen = await getCurrentScreen(); + let screenWidth = curScreen.bounds.width + let screenHeight = curScreen.bounds.height + desktopCapturer.getSources({ + types: ['screen'], + thumbnailSize: { + width: screenWidth * curScreen.scaleFactor, + height: screenHeight * curScreen.scaleFactor, + } + }).then((sources) => { + let imgSrc = sources.filter(s => s.id.indexOf(curScreen.id) >= 0)[0].thumbnail.toDataURL() + callback(imgSrc) + }) +} diff --git a/static/plugins/capture/index.html b/static/plugins/capture/index.html new file mode 100644 index 0000000..77aa1f9 --- /dev/null +++ b/static/plugins/capture/index.html @@ -0,0 +1,88 @@ + + + + + + + Document + + + + +
+
+ +
+
+
+
+
+
+
+ + + + diff --git a/static/plugins/capture/logo.png b/static/plugins/capture/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..50eddd8a77d18f9441263a98cb50be6d8aae0ef5 GIT binary patch literal 3488 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 YXh-{ldg3dGEdVbIf{iKD { + let { x, y } = currentWindow.getBounds(); + ipcRenderer.send('capture-screen', { + type: 'getAllDisplays', + winId: currentWindow.id, + x, + y, + }); + return new Promise(resolve => { + ipcRenderer.on('getAllDisplays', (e, { type, winId, screen}) => { + if (winId === currentWindow.id) { + resolve(screen) + } + }) + }) +}