feat: 支持截屏,下载方式调整

This commit is contained in:
muwoo 2021-06-22 15:35:46 +08:00
parent 1e77613565
commit e3838738db
27 changed files with 1519 additions and 12 deletions

View File

@ -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",

View File

@ -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,
};
};

View File

@ -1,4 +1,5 @@
module.exports = () => ({
picker: require("./picker")(),
separator: require("./separate")(),
capture: require("./capture")(),
});

View File

@ -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');
});

View File

@ -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,

View File

@ -1,4 +1,4 @@
export default {
development: 'http://localhost:7001',
development: 'http://rubick-server.qa.91jkys.com',
production: 'http://rubick-server.qa.91jkys.com',
};

View File

@ -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',
}
]

View File

@ -9,5 +9,10 @@ export default {
pick() {
ipcRenderer.send('start-picker')
}
},
'rubick-screen-short-cut': {
shortCut() {
ipcRenderer.send('capture-screen', {type: 'start'})
}
}
}

View File

@ -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,
}

View File

@ -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(),

Binary file not shown.

View File

@ -0,0 +1,370 @@
*{margin: 0;padding: 0;list-style: none;}
/*
KISSY CSS Reset
理念1. reset 的目的不是清除浏览器的默认样式这仅是部分工作清除和重置是紧密不可分的
2. reset 的目的不是让默认样式在所有浏览器下一致而是减少默认样式有可能带来的问题
3. reset 期望提供一套普适通用的基础样式但没有银弹推荐根据具体需求裁剪和修改后再使用
特色1. 适应中文2. 基于最新主流浏览器
维护玉伯<lifesinger@gmail.com>, 正淳<ragecarrier@gmail.com>
*/
/** 清除内外边距 **/
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;
}

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>IconFont</title>
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="iconfont.css">
</head>
<body>
<div class="main markdown">
<h1>IconFont 图标</h1>
<ul class="icon_lists clear">
<li>
<i class="icon iconfont icon-xiazai"></i>
<div class="name">下载</div>
<div class="fontclass">.icon-xiazai</div>
</li>
<li>
<i class="icon iconfont icon-guanbi"></i>
<div class="name">关闭</div>
<div class="fontclass">.icon-guanbi</div>
</li>
<li>
<i class="icon iconfont icon-zhongzhi"></i>
<div class="name">重置</div>
<div class="fontclass">.icon-zhongzhi</div>
</li>
<li>
<i class="icon iconfont icon-duihao"></i>
<div class="name">对号</div>
<div class="fontclass">.icon-duihao</div>
</li>
</ul>
<h2 id="font-class-">font-class引用</h2>
<hr>
<p>font-class是unicode使用方式的一种变种主要是解决unicode书写不直观语意不明确的问题。</p>
<p>与unicode使用方式相比具有如下特点</p>
<ul>
<li>兼容性良好支持ie8+,及所有现代浏览器。</li>
<li>相比于unicode语意明确书写更直观。可以很容易分辨这个icon是什么。</li>
<li>因为使用class来定义图标所以当要替换图标时只需要修改class里面的unicode引用。</li>
<li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-fontclass-">第一步引入项目下面生成的fontclass代码</h3>
<pre><code class="lang-js hljs javascript"><span class="hljs-comment">&lt;link rel="stylesheet" type="text/css" href="./iconfont.css"&gt;</span></code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="lang-css hljs">&lt;<span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">class</span>="<span class="hljs-selector-tag">iconfont</span> <span class="hljs-selector-tag">icon-xxx</span>"&gt;&lt;/<span class="hljs-selector-tag">i</span>&gt;</code></pre>
<blockquote>
<p>"iconfont"是你项目下的font-family。可以通过编辑项目查看默认是"iconfont"。</p>
</blockquote>
</div>
</body>
</html>

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>IconFont</title>
<link rel="stylesheet" href="demo.css">
<script src="iconfont.js"></script>
<style type="text/css">
.icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em; height: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
</style>
</head>
<body>
<div class="main markdown">
<h1>IconFont 图标</h1>
<ul class="icon_lists clear">
<li>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-xiazai"></use>
</svg>
<div class="name">下载</div>
<div class="fontclass">#icon-xiazai</div>
</li>
<li>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-guanbi"></use>
</svg>
<div class="name">关闭</div>
<div class="fontclass">#icon-guanbi</div>
</li>
<li>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zhongzhi"></use>
</svg>
<div class="name">重置</div>
<div class="fontclass">#icon-zhongzhi</div>
</li>
<li>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-duihao"></use>
</svg>
<div class="name">对号</div>
<div class="fontclass">#icon-duihao</div>
</li>
</ul>
<h2 id="symbol-">symbol引用</h2>
<hr>
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
这种用法其实是做了一个svg的集合与另外两种相比具有如下特点</p>
<ul>
<li>支持多色图标了,不再受单色限制。</li>
<li>通过一些技巧,支持像字体那样,通过<code>font-size</code>,<code>color</code>来调整样式。</li>
<li>兼容性较差,支持 ie9+,及现代浏览器。</li>
<li>浏览器渲染svg的性能一般还不如png。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-symbol-">第一步引入项目下面生成的symbol代码</h3>
<pre><code class="lang-js hljs javascript"><span class="hljs-comment">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;</span></code></pre>
<h3 id="-css-">第二步加入通用css代码引入一次就行</h3>
<pre><code class="lang-js hljs javascript">&lt;style type=<span class="hljs-string">"text/css"</span>&gt;
.icon {
width: <span class="hljs-number">1</span>em; height: <span class="hljs-number">1</span>em;
vertical-align: <span class="hljs-number">-0.15</span>em;
fill: currentColor;
overflow: hidden;
}
&lt;<span class="hljs-regexp">/style&gt;</span></code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="lang-js hljs javascript">&lt;svg <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"icon"</span> aria-hidden=<span class="hljs-string">"true"</span>&gt;<span class="xml"><span class="hljs-tag">
&lt;<span class="hljs-name">use</span> <span class="hljs-attr">xlink:href</span>=<span class="hljs-string">"#icon-xxx"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">use</span>&gt;</span>
</span>&lt;<span class="hljs-regexp">/svg&gt;
</span></code></pre>
</div>
</body>
</html>

View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>IconFont</title>
<link rel="stylesheet" href="demo.css">
<style type="text/css">
@font-face {font-family: "iconfont";
src: url('iconfont.eot'); /* IE9*/
src: url('iconfont.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfont.woff') format('woff'), /* chrome, firefox */
url('iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
}
.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;
}
</style>
</head>
<body>
<div class="main markdown">
<h1>IconFont 图标</h1>
<ul class="icon_lists clear">
<li>
<i class="icon iconfont">&#xe627;</i>
<div class="name">下载</div>
<div class="code">&amp;#xe627;</div>
</li>
<li>
<i class="icon iconfont">&#xe66c;</i>
<div class="name">关闭</div>
<div class="code">&amp;#xe66c;</div>
</li>
<li>
<i class="icon iconfont">&#xe633;</i>
<div class="name">重置</div>
<div class="code">&amp;#xe633;</div>
</li>
<li>
<i class="icon iconfont">&#xeeda;</i>
<div class="name">对号</div>
<div class="code">&amp;#xeeda;</div>
</li>
</ul>
<h2 id="unicode-">unicode引用</h2>
<hr>
<p>unicode是字体在网页端最原始的应用方式特点是</p>
<ul>
<li>兼容性最好支持ie6+,及所有现代浏览器。</li>
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
<li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
</ul>
<blockquote>
<p>注意新版iconfont支持多色图标这些多色图标在unicode模式下将不能使用如果有需求建议使用symbol的引用方式</p>
</blockquote>
<p>unicode使用步骤如下</p>
<h3 id="-font-face">第一步拷贝项目下面生成的font-face</h3>
<pre><code class="lang-js hljs javascript">@font-face {
font-family: <span class="hljs-string">'iconfont'</span>;
src: url(<span class="hljs-string">'iconfont.eot'</span>);
src: url(<span class="hljs-string">'iconfont.eot?#iefix'</span>) format(<span class="hljs-string">'embedded-opentype'</span>),
url(<span class="hljs-string">'iconfont.woff'</span>) format(<span class="hljs-string">'woff'</span>),
url(<span class="hljs-string">'iconfont.ttf'</span>) format(<span class="hljs-string">'truetype'</span>),
url(<span class="hljs-string">'iconfont.svg#iconfont'</span>) format(<span class="hljs-string">'svg'</span>);
}
</code></pre>
<h3 id="-iconfont-">第二步定义使用iconfont的样式</h3>
<pre><code class="lang-js hljs javascript">.iconfont{
font-family:<span class="hljs-string">"iconfont"</span> !important;
font-size:<span class="hljs-number">16</span>px;font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: <span class="hljs-number">0.2</span>px;
-moz-osx-font-smoothing: grayscale;
}
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre><code class="lang-js hljs javascript">&lt;i <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"iconfont"</span>&gt;&amp;#x33;<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span></span></code></pre>
<blockquote>
<p>"iconfont"是你项目下的font-family。可以通过编辑项目查看默认是"iconfont"。</p>
</blockquote>
</div>
</body>
</html>

View File

@ -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"; }

Binary file not shown.

View File

@ -0,0 +1 @@
(function(window){var svgSprite='<svg><symbol id="icon-xiazai" viewBox="0 0 1024 1024"><path d="M947.2 588.8c-19.2 0-32 12.8-32 32v108.8c0 51.2-25.6 96-96 96H204.8c-76.8 6.4-96-44.8-96-96V620.8c0-19.2-12.8-32-32-32s-25.6 12.8-25.6 32V768c0 70.4 57.6 128 128 128h672c70.4 0 128-57.6 128-128V620.8c-6.4-19.2-19.2-32-32-32zM518.4 716.8" fill="" ></path><path d="M544 710.4l172.8-166.4c12.8-12.8 12.8-32 0-44.8s-32-12.8-44.8 0L550.4 608v-448c0-19.2-12.8-32-32-32s-32 12.8-32 32v448L364.8 499.2c-12.8-12.8-38.4-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l172.8 166.4s25.6 25.6 51.2 0z" fill="" ></path></symbol><symbol id="icon-guanbi" viewBox="0 0 1024 1024"><path d="M220.3136 256.512l579.2768 579.2768a25.6 25.6 0 1 0 36.1984-36.1984L256.512 220.3136a25.6 25.6 0 1 0-36.1984 36.1984z" ></path><path d="M799.5904 220.3136L220.3136 799.5904a25.6 25.6 0 1 0 36.1984 36.1984L835.7888 256.512a25.6 25.6 0 1 0-36.1984-36.1984z" ></path></symbol><symbol id="icon-zhongzhi" viewBox="0 0 1024 1024"><path d="M931.07 384.75a368 368 0 0 0-704 95.25H64l192 192 192-192H288.91C312 333.51 439.12 221.13 592 221.13c169.21 0 306.87 137.66 306.87 306.87S761.21 834.87 592 834.87a307.37 307.37 0 0 1-194.56-69.55 30.57 30.57 0 0 0-38.79 47.25 368.1 368.1 0 0 0 572.42-427.82z" ></path></symbol><symbol id="icon-duihao" viewBox="0 0 1024 1024"><path d="M461.28 813.14a30 30 0 0 1-18.89-6.69L145 565.48a30 30 0 1 1 37.78-46.61l273.35 221.5 382.5-502.29a30 30 0 1 1 47.73 36.35L485.15 801.32a30 30 0 0 1-20.29 11.61 30.83 30.83 0 0 1-3.58 0.21z" ></path></symbol></svg>';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("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window)

View File

@ -0,0 +1,38 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="xiazai" unicode="&#58919;" d="M947.2 307.2c-19.2 0-32-12.8-32-32v-108.8c0-51.2-25.6-96-96-96H204.8c-76.8-6.4-96 44.8-96 96V275.2c0 19.2-12.8 32-32 32s-25.6-12.8-25.6-32V128c0-70.4 57.6-128 128-128h672c70.4 0 128 57.6 128 128V275.2c-6.4 19.2-19.2 32-32 32zM518.4 179.2M544 185.6l172.8 166.4c12.8 12.8 12.8 32 0 44.8s-32 12.8-44.8 0L550.4 288v448c0 19.2-12.8 32-32 32s-32-12.8-32-32v-448L364.8 396.8c-12.8 12.8-38.4 12.8-44.8 0-12.8-12.8-12.8-32 0-44.8l172.8-166.4s25.6-25.6 51.2 0z" horiz-adv-x="1024" />
<glyph glyph-name="guanbi" unicode="&#58988;" d="M220.3136 639.488l579.2768-579.2768a25.6 25.6 0 1 1 36.1984 36.1984L256.512 675.6864a25.6 25.6 0 1 1-36.1984-36.1984zM799.5904 675.6864L220.3136 96.4096a25.6 25.6 0 1 1 36.1984-36.1984L835.7888 639.488a25.6 25.6 0 1 1-36.1984 36.1984z" horiz-adv-x="1024" />
<glyph glyph-name="zhongzhi" unicode="&#58931;" d="M931.07 511.25a368 368 0 0 1-704-95.25H64l192-192 192 192H288.91C312 562.49 439.12 674.87 592 674.87c169.21 0 306.87-137.66 306.87-306.87S761.21 61.13 592 61.13a307.37 307.37 0 0 0-194.56 69.55 30.57 30.57 0 0 1-38.79-47.25 368.1 368.1 0 0 1 572.42 427.82z" horiz-adv-x="1024" />
<glyph glyph-name="duihao" unicode="&#61146;" d="M461.28 82.86a30 30 0 0 0-18.89 6.69L145 330.52a30 30 0 1 0 37.78 46.61l273.35-221.5 382.5 502.29a30 30 0 1 0 47.73-36.35L485.15 94.68a30 30 0 0 0-20.29-11.61 30.83 30.83 0 0 0-3.58-0.21z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,411 @@
const Event = require('events')
const { getCurrentScreen } = require('./utils')
const CREATE_RECT = 1
const MOVING_RECT = 2
const RESIZE = 3
const ANCHORS = [
{ row: 'x', col: 'y', cursor: 'nwse-resize' },
{ row: '', col: 'y', cursor: 'ns-resize' },
{ row: 'r', col: 'y', cursor: 'nesw-resize' },
{ row: 'x', col: '', cursor: 'ew-resize' },
{ row: 'r', col: '', cursor: 'ew-resize' },
{ row: 'x', col: 'b', cursor: 'nesw-resize' },
{ row: '', col: 'b', cursor: 'ns-resize' },
{ row: 'r', col: 'b', cursor: 'nwse-resize' },
]
class CaptureEditor extends Event {
constructor($canvas, $bg, imageSrc) {
super()
this.$canvas = $canvas
this.imageSrc = imageSrc
this.disabled = false
let currentScreen;
(async () => {
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

View File

@ -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()
}
})
})

View File

@ -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)
})
}

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
@import "./assets/iconfont/iconfont.css";
html, body, div {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
}
.bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.image-canvas {
position: absolute;
display: none;
z-index: 1;
}
.size-info {
position: absolute;
color: #ffffff;
font-size: 12px;
background: rgba(40, 40, 40, 0.8);
padding: 5px 10px;
border-radius: 2px;
font-family: Arial Consolas sans-serif;
display: none;
z-index: 2;
}
.toolbar {
position: absolute;
color: #343434;
font-size: 12px;
background: #f5f5f5;
padding: 5px 10px;
border-radius: 4px;
font-family: Arial Consolas sans-serif;
display: none;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);
z-index: 2;
align-items: center;
}
.toolbar .iconfont {
font-size: 24px;
padding: 2px 5px;
}
</style>
</head>
<body>
<div id="js-bg" class="bg"></div>
<div id="js-mask" class="mask"></div>
<canvas id="js-canvas" class="image-canvas"></canvas>
<div id="js-size-info" class="size-info"></div>
<div id="js-toolbar" class="toolbar">
<div class="iconfont icon-zhongzhi" id="js-tool-reset"></div>
<div class="iconfont icon-xiazai" id="js-tool-save"></div>
<div class="iconfont icon-guanbi" id="js-tool-close"></div>
<div class="iconfont icon-duihao" id="js-tool-ok"></div>
</div>
<script src="./capture-renderer.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,20 @@
const { remote, ipcRenderer } = require('electron');
let currentWindow = remote.getCurrentWindow()
exports.getCurrentScreen = () => {
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)
}
})
})
}