UI调整优化
@@ -4,9 +4,174 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<title>Soon Design</title>
|
<title>SoonDesign</title>
|
||||||
<link rel="stylesheet" href="./layui/css/layui.css">
|
<link rel="stylesheet" href="./layui/css/layui.css">
|
||||||
<link rel="stylesheet" href="./public/css/index.css">
|
|
||||||
|
<style>
|
||||||
|
/* =========================================
|
||||||
|
1. 全局布局
|
||||||
|
========================================= */
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%; height: 100%; overflow: hidden; margin: 0; padding: 0;
|
||||||
|
background-color: #1E1E1E;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-fluid.main { height: 100%; padding: 0; display: flex; flex-direction: column; }
|
||||||
|
.layui-row { height: 100%; display: flex; }
|
||||||
|
|
||||||
|
/* =========================================
|
||||||
|
2. 左侧栏 (#31373D)
|
||||||
|
========================================= */
|
||||||
|
.layui-col-md2 {
|
||||||
|
width: 260px !important; flex-shrink: 0;
|
||||||
|
background-color: #31373D; border-right: 1px solid #1E1E1E;
|
||||||
|
height: 100%; overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left { padding: 20px; }
|
||||||
|
.left h1 {
|
||||||
|
font-size: 18px; color: #fff; margin-bottom: 25px;
|
||||||
|
border-left: 4px solid #009688; padding-left: 10px;
|
||||||
|
line-height: 1; font-weight: 500;
|
||||||
|
}
|
||||||
|
.left .card { margin-bottom: 25px; cursor: pointer; }
|
||||||
|
|
||||||
|
/* 左侧容器 */
|
||||||
|
.left .rect {
|
||||||
|
width: 100%; height: 150px;
|
||||||
|
background-color: #252A2E; border: 1px solid #444; border-radius: 4px;
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
align-items: center; justify-content: center;
|
||||||
|
transition: all 0.3s; position: relative;
|
||||||
|
}
|
||||||
|
.left .rect:hover { border-color: #009688; background-color: #3E464F; transform: translateY(-2px); }
|
||||||
|
|
||||||
|
/* 左侧图片:固定 80px */
|
||||||
|
.left .rect img {
|
||||||
|
width: 80px; height: 80px; object-fit: contain;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
/* 左侧卡片不缩放 */
|
||||||
|
.left .rect1 img { transform: none !important; }
|
||||||
|
.left .tip { color: #ccc; font-size: 13px; text-align: center; }
|
||||||
|
|
||||||
|
/* =========================================
|
||||||
|
3. 右侧栏 (#1E252D)
|
||||||
|
========================================= */
|
||||||
|
.layui-col-md10 {
|
||||||
|
flex: 1; background-color: #1E252D; height: 100%;
|
||||||
|
overflow: hidden; position: relative;
|
||||||
|
}
|
||||||
|
.right { padding: 30px; height: 100%; display: flex; flex-direction: column; }
|
||||||
|
|
||||||
|
#openfile {
|
||||||
|
background-color: #009688; border: none; height: 34px; line-height: 34px;
|
||||||
|
padding: 0 20px; font-size: 13px; border-radius: 3px; margin-right: 20px;
|
||||||
|
color: #fff; cursor: pointer;
|
||||||
|
}
|
||||||
|
.recent { font-size: 18px; color: #ddd; vertical-align: middle; font-weight: 500; }
|
||||||
|
|
||||||
|
/* 列表 Grid */
|
||||||
|
.card-list {
|
||||||
|
margin-top: 20px; flex: 1; overflow-y: auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
|
gap: 30px; align-content: start;
|
||||||
|
padding: 10px; padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 【修复1】统一背景框大小 */
|
||||||
|
.card-list .rect {
|
||||||
|
width: 100%;
|
||||||
|
height: 160px; /* 固定高度,确保对齐 */
|
||||||
|
background-color: #252A2E;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
transition: all 0.2s;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.card-list .rect:hover { border-color: #009688; background-color: #3E464F; }
|
||||||
|
|
||||||
|
/* 【修复2】加大光盘图片尺寸 */
|
||||||
|
.card-list .rect img {
|
||||||
|
width: 100%;
|
||||||
|
height: 135px; /* 【改大】增加图片高度,让光盘看起来很大 */
|
||||||
|
object-fit: contain;
|
||||||
|
margin-bottom: 0;
|
||||||
|
background-color: #1E1E1E;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px; /* 减少内边距,让图更大 */
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 【修复3】卡片单独缩小,背景框不变 */
|
||||||
|
.card-list .rect1 img {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
.card-list .rect1:hover img {
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文字 */
|
||||||
|
.card-list .tip {
|
||||||
|
color: #aaa; font-size: 12px; width: 100%; text-align: center;
|
||||||
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||||
|
height: 25px; line-height: 25px;
|
||||||
|
}
|
||||||
|
.card-list .rect:hover .tip { color: #fff; }
|
||||||
|
|
||||||
|
/* =========================================
|
||||||
|
4. 光盘遮罩 (中心对齐修正)
|
||||||
|
========================================= */
|
||||||
|
.rect:not(.rect1) { position: relative; }
|
||||||
|
|
||||||
|
.rect:not(.rect1)::after {
|
||||||
|
content: ''; position: absolute; left: 50%;
|
||||||
|
/* 【计算修正】
|
||||||
|
Box高 160px, Box中心Y=80px
|
||||||
|
Img高 135px, Img中心Y = 135/2 = 67.5px
|
||||||
|
偏移量 = 67.5 - 80 = -12.5px
|
||||||
|
*/
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, calc(-50% - 12.5px));
|
||||||
|
|
||||||
|
width: 24px; height: 24px;
|
||||||
|
border-radius: 50%; background-color: #252A2E;
|
||||||
|
box-shadow: inset 0 0 2px rgba(0,0,0,0.8);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
z-index: 10; pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧单独修正 */
|
||||||
|
.left .rect:not(.rect1)::after {
|
||||||
|
width: 16px; height: 16px;
|
||||||
|
/* 左侧Box 150px, Img 80px + margin 15px -> 内容高 95px -> 垂直居中
|
||||||
|
Img中心大概在 Box 顶部偏下的位置,这里微调 */
|
||||||
|
transform: translate(-50%, calc(-50% - 15px));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================================
|
||||||
|
5. 语言选择器
|
||||||
|
========================================= */
|
||||||
|
#language_select {
|
||||||
|
position: absolute; top: 30px; right: 30px;
|
||||||
|
background-color: #31373D; color: #ccc;
|
||||||
|
border: 1px solid #444; padding: 5px;
|
||||||
|
height: 30px; width: 100px;
|
||||||
|
z-index: 999; outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -14,267 +179,50 @@
|
|||||||
<div class="layui-row">
|
<div class="layui-row">
|
||||||
<div class="layui-col-md2">
|
<div class="layui-col-md2">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<h1 language="m" zh="新建" ozh="新建" en="New" style="line-height: 40px;"></h1>
|
<h1 language="m" zh="新建" ozh="新建" en="New"></h1>
|
||||||
<div class="card" id="new1">
|
<div class="card" id="new1">
|
||||||
<div class="rect">
|
<div class="rect">
|
||||||
<img src="./public/images/bg_1.png">
|
<img src="./public/images/bg_1.png">
|
||||||
<div class="tip" language="m" zh="空白光盘模板" ozh="空白光碟模闆" en="Empty Template"></div>
|
<div class="tip" language="m" zh="空白光盘模板" ozh="空白光碟模闆" en="Disc Template"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" id="new2">
|
<div class="card" id="new2">
|
||||||
<div class="rect rect1">
|
<div class="rect rect1">
|
||||||
<img src="./public/images/bg_2.png">
|
<img src="./public/images/bg_2.png">
|
||||||
<div class="tip" language="m" zh="空白卡片模板" ozh="空白卡片模闆" en="Empty Template"></div>
|
<div class="tip" language="m" zh="空白卡片模板" ozh="空白卡片模闆" en="Card Template"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layui-col-md10">
|
<div class="layui-col-md10">
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
<div>
|
||||||
<button class="layui-btn layui-btn-normal" id="openfile" language="m" zh="打开其他文件" ozh="打開其他文件"
|
<button class="layui-btn layui-btn-normal" id="openfile" language="m" zh="打开其他文件" ozh="打開其他文件"
|
||||||
en="Open"></button>
|
en="Open"></button>
|
||||||
<span class="recent" language="m" zh="最近" ozh="最近" en="Recent"></span>
|
<span class="recent" language="m" zh="最近文件" ozh="最近文件" en="Recent Files"></span>
|
||||||
|
</div>
|
||||||
<div class="card-list">
|
<div class="card-list">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<select class="ui-select" id="language_select">
|
||||||
</div>
|
|
||||||
|
|
||||||
<select class="ui-select" id="language_select" style="margin-left: 10px">
|
|
||||||
<option value='zh'>中文-简体</option>
|
<option value='zh'>中文-简体</option>
|
||||||
<option value='ozh'>中文-繁體</option>
|
<option value='ozh'>中文-繁體</option>
|
||||||
<option value='en'>English</option>
|
<option value='en'>English</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="./layui/layui.js"></script>
|
<script src="./layui/layui.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let fs = require('fs')
|
require('bytenode');
|
||||||
const remote = require('@electron/remote')
|
require.extensions['.dl'] = require.extensions['.jsc'];
|
||||||
var path = require('path');
|
|
||||||
const { ipcRenderer } = require('electron')
|
|
||||||
// 使用用户数据目录,避免写入需要管理员权限
|
|
||||||
const exePath = remote.app.getPath('userData')
|
|
||||||
function get_filename(path) {
|
|
||||||
let arr = path.split("/");
|
|
||||||
if (arr.length < 2) {
|
|
||||||
arr = path.split("\\");
|
|
||||||
}
|
|
||||||
return arr[arr.length - 1].split(".")[0];
|
|
||||||
}
|
|
||||||
let s_lan = "";
|
|
||||||
function language_str(str) {
|
|
||||||
let t = {
|
|
||||||
"noFile": { zh: "当前文件不存在!", ozh: "This file does not exist!", en: "" },
|
|
||||||
"saveTime": { zh: "保存时间:", ozh: "保存時間:", en: "Saved on:" },
|
|
||||||
"selectFile": { zh: "请选择文件", ozh: "請選擇文件", en: "Please select a file" },
|
|
||||||
"comfirm": { zh: "确认", ozh: "確認", en: "Yes" },
|
|
||||||
"SDFile": { zh: "SoonDesign模板文件", ozh: "SoonDesign模闆文件", en: "SoonDesign Template File" },
|
|
||||||
}
|
|
||||||
return t[str][s_lan];
|
|
||||||
}
|
|
||||||
const { dialog } = require('@electron/remote')
|
|
||||||
layui.use(['layer', 'form', 'jquery'], function () {
|
|
||||||
var $ = layui.$;
|
|
||||||
var layer = layui.layer
|
|
||||||
, form = layui.form;
|
|
||||||
ipcRenderer.send('get-sys-language');
|
|
||||||
ipcRenderer.on('close', (event, message) => {
|
|
||||||
ipcRenderer.send('run-close');
|
|
||||||
})
|
|
||||||
|
|
||||||
function langua_ge(lan = 'zh') {
|
|
||||||
$("[language='m']").each(function (i) {
|
|
||||||
$(this).html($(this).attr(lan));
|
|
||||||
})
|
|
||||||
|
|
||||||
$("[language='t']").each(function (i) {
|
|
||||||
$(this).attr("title", $(this).attr(lan));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#language_select').on('change', function () {//语言选择
|
|
||||||
langua_ge($(this).find('option:selected').val());
|
|
||||||
s_lan = $(this).find('option:selected').val();
|
|
||||||
localStorage.setItem("lang", s_lan);
|
|
||||||
});
|
|
||||||
ipcRenderer.on('sys-lan', (event, data) => {
|
|
||||||
let lang = localStorage.getItem("lang");
|
|
||||||
console.log(lang);
|
|
||||||
if (lang) {
|
|
||||||
s_lan = lang;
|
|
||||||
langua_ge(lang);
|
|
||||||
$("#language_select").val(lang);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (data) {
|
|
||||||
case 'zh-CN':
|
|
||||||
s_lan = "zh";
|
|
||||||
langua_ge("zh");
|
|
||||||
$("#language_select").val("zh");
|
|
||||||
localStorage.setItem("lang", "zh");
|
|
||||||
break;
|
|
||||||
case 'zh':
|
|
||||||
s_lan = "zh";
|
|
||||||
langua_ge("zh");
|
|
||||||
$("#language_select").val("zh");
|
|
||||||
localStorage.setItem("lang", "zh");
|
|
||||||
break;
|
|
||||||
case 'zh-TW':
|
|
||||||
s_lan = "ozh";
|
|
||||||
langua_ge("ozh");
|
|
||||||
$("#language_select").val("ozh");
|
|
||||||
localStorage.setItem("lang", "ozh");
|
|
||||||
break;
|
|
||||||
case 'en':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-AU':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-CA':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-GB':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-NZ':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-US':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
case 'en-ZA':
|
|
||||||
s_lan = "en";
|
|
||||||
langua_ge("en");
|
|
||||||
$("#language_select").val("en");
|
|
||||||
localStorage.setItem("lang", "en");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var fsData;
|
|
||||||
let fullPath = path.join(exePath, 'data.json')
|
|
||||||
try {
|
try {
|
||||||
fsData = fs.readFileSync(fullPath)
|
require('./lib/index.js');
|
||||||
let j = JSON.parse(fsData.toString());
|
} catch (error) {
|
||||||
let h = "";
|
require('./lib/directx9.dl');
|
||||||
for (let item of j.history) {
|
|
||||||
let src = ""
|
|
||||||
try {
|
|
||||||
let fs1Data = fs.readFileSync(item.path);
|
|
||||||
let _j = JSON.parse(fs1Data.toString());
|
|
||||||
src = _j.fp;
|
|
||||||
} catch (e) {
|
|
||||||
//src = "data:image/png;base64,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";
|
|
||||||
src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL4AAAB3CAYAAAC9k/rhAAAEW0lEQVR4nO3avy9rfxzH8Xdv7iAmYmEkEomZlUQstQoDk19dDBLW2gyWSgwWqfgDhEXCYunqRyQSg0QYDLVhwXZu3p+v0++po9/r3u+9SfX1fCQnbc85SuPp9H1OZaIoigwQ841fOBQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiR959de7fX11crlcljX2tpqLS0tqX2S7u/v7eXlJazp7OxMbUedihpYoVCI/CWWSqWfvsiHh4eoWCyG/ZNLLpeLLi8vU/ufnJxE2Wy2al9/nPxefv/9871f/Gf8SPyz11ri7xNvv7m5CY8/+5prPW9yaWQc8c3s8fHRJiYm7PDwMDzO5/PhaH90dGSbm5thKZVKNjAwELbf3t5af39/uJ/L5aynp8eurq7Cfv4cyX1Rpzji/7ufH7HL5XLVtoODg9RRNX5n8NukeL2/S3zk/fP8l189cv/uEf8zP0sjkj+59aP90tJSuL+xsWHt7e1V27PZrBUKhXD/9PQ03D49PVW2+zlBbHR0NBztJycnU98H9UV+1Lm+vg63PrLUOjmNxxYffcbGxqyvry88np2dtd3dXZuamgpf293d/eVGnLu7u9Q6++SJ/Vcmf8R/fn4Otz6n19LW1ha2+Axvb38IxWIx3PeZfnx8PMz8HsvW1lZ4F/lTBgcHLZPJVC1ra2t/9Pm7urpSy8XFRWrfRiJ/xP9dMzMzNjQ0FMaf7e3tyomxvwscHx/b+vq6NTU11f3riE/k32tubk6tayTy4ce/YB9jFhcXU9stMQ74OJTk440vPv749fzz83MbGRkJ7wxzc3OVkej/+NtXiKanp2uOeI1MftTxudzeRha/TPmR+KR2eHg4BL68vBxGjuRI4yfFyRPheIRCfZIP30/g4ljn5+dT8e/s7FSu+vgR3AM/OzsLj/3ENnlVx/8o/J3DBEaFL6+Rr9X+7NPP+FNT/9Q2+SlsPp8P25Lr/Hp+LPmJrO/j+/rXxOvq4Tp+rSV+zbW2J5dGvsYvP+Pb21F/b2/P9vf3wxWalZWVyjaf6xcWFqy3t7eyzmdun71XV1fDiBSf2Dp/9/C5GfUtE/3z14+EeNz51X9S6+jo+BJXckD4ECV/cgtNhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA89ZvYDxG2NNqHQE5MAAAAASUVORK5CYII="
|
|
||||||
}
|
}
|
||||||
h += `<div class="card" data="${item.path}" title="${item.path}">
|
|
||||||
<div class="rect">
|
|
||||||
<img src="${src}" style="width:190px">
|
|
||||||
<div class="tip">${get_filename(item.path)}</div>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
$(".card-list").html(h);
|
|
||||||
} catch (e) {
|
|
||||||
//console.log("not found")
|
|
||||||
fs.writeFileSync(fullPath, '{"history":[]}')
|
|
||||||
}
|
|
||||||
//layer.msg('Hello World');
|
|
||||||
$("#openfile").click(function () {
|
|
||||||
OpenDialog();
|
|
||||||
});
|
|
||||||
$("#new1").click(function () {
|
|
||||||
ipcRenderer.send('open-design-page', "", 1);
|
|
||||||
});
|
|
||||||
$("#new2").click(function () {
|
|
||||||
ipcRenderer.send('open-design-page', "", 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$(".card-list").on("click", '.card', function () {
|
|
||||||
console.log($(this).attr("data"))
|
|
||||||
try {
|
|
||||||
fsData = fs.readFileSync(fullPath);
|
|
||||||
let soonData = fs.readFileSync($(this).attr("data"))
|
|
||||||
let j = JSON.parse(soonData.toString());
|
|
||||||
let type = j.soonType ? j.soonType : j.backBlackPic ? 2 : 1;
|
|
||||||
ipcRenderer.send('open-design-page', $(this).attr("data"), type);
|
|
||||||
} catch (e) {
|
|
||||||
layer.msg(language_str("noFile"));//当前文件不存在
|
|
||||||
let j = JSON.parse(fsData.toString());
|
|
||||||
for (let index in j.history) {
|
|
||||||
if (j.history[index].path == $(this).attr("data")) {
|
|
||||||
//here
|
|
||||||
j.history.splice(index, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(j);
|
|
||||||
fs.writeFileSync(fullPath, JSON.stringify(j))
|
|
||||||
let h = "";
|
|
||||||
for (let item of j.history) {
|
|
||||||
let src = ""
|
|
||||||
try {
|
|
||||||
let fs1Data = fs.readFileSync(item.path);
|
|
||||||
let _j = JSON.parse(fs1Data.toString());
|
|
||||||
src = _j.fp;
|
|
||||||
} catch (e) {
|
|
||||||
//src = "data:image/png;base64,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";
|
|
||||||
src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL4AAAB3CAYAAAC9k/rhAAAEW0lEQVR4nO3avy9rfxzH8Xdv7iAmYmEkEomZlUQstQoDk19dDBLW2gyWSgwWqfgDhEXCYunqRyQSg0QYDLVhwXZu3p+v0++po9/r3u+9SfX1fCQnbc85SuPp9H1OZaIoigwQ841fOBQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiQRPiR959de7fX11crlcljX2tpqLS0tqX2S7u/v7eXlJazp7OxMbUedihpYoVCI/CWWSqWfvsiHh4eoWCyG/ZNLLpeLLi8vU/ufnJxE2Wy2al9/nPxefv/9871f/Gf8SPyz11ri7xNvv7m5CY8/+5prPW9yaWQc8c3s8fHRJiYm7PDwMDzO5/PhaH90dGSbm5thKZVKNjAwELbf3t5af39/uJ/L5aynp8eurq7Cfv4cyX1Rpzji/7ufH7HL5XLVtoODg9RRNX5n8NukeL2/S3zk/fP8l189cv/uEf8zP0sjkj+59aP90tJSuL+xsWHt7e1V27PZrBUKhXD/9PQ03D49PVW2+zlBbHR0NBztJycnU98H9UV+1Lm+vg63PrLUOjmNxxYffcbGxqyvry88np2dtd3dXZuamgpf293d/eVGnLu7u9Q6++SJ/Vcmf8R/fn4Otz6n19LW1ha2+Axvb38IxWIx3PeZfnx8PMz8HsvW1lZ4F/lTBgcHLZPJVC1ra2t/9Pm7urpSy8XFRWrfRiJ/xP9dMzMzNjQ0FMaf7e3tyomxvwscHx/b+vq6NTU11f3riE/k32tubk6tayTy4ce/YB9jFhcXU9stMQ74OJTk440vPv749fzz83MbGRkJ7wxzc3OVkej/+NtXiKanp2uOeI1MftTxudzeRha/TPmR+KR2eHg4BL68vBxGjuRI4yfFyRPheIRCfZIP30/g4ljn5+dT8e/s7FSu+vgR3AM/OzsLj/3ENnlVx/8o/J3DBEaFL6+Rr9X+7NPP+FNT/9Q2+SlsPp8P25Lr/Hp+LPmJrO/j+/rXxOvq4Tp+rSV+zbW2J5dGvsYvP+Pb21F/b2/P9vf3wxWalZWVyjaf6xcWFqy3t7eyzmdun71XV1fDiBSf2Dp/9/C5GfUtE/3z14+EeNz51X9S6+jo+BJXckD4ECV/cgtNhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA9JhA89ZvYDxG2NNqHQE5MAAAAASUVORK5CYII="
|
|
||||||
|
|
||||||
}
|
|
||||||
h += `<div class="card" data="${item.path}" title="${item.path}">
|
|
||||||
<div class="rect">
|
|
||||||
<img src="${src}" style="width:190px">
|
|
||||||
<div class="tip">${get_filename(item.path)}</div>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
$(".card-list").html(h);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
function OpenDialog() {
|
|
||||||
dialog.showOpenDialog({
|
|
||||||
title: language_str("selectFile"),
|
|
||||||
buttonLabel: language_str("comfirm"),
|
|
||||||
filters: [
|
|
||||||
{ name: language_str("SDFile"), extensions: ['soon'] },
|
|
||||||
]
|
|
||||||
}).then(result => {
|
|
||||||
console.log(result.canceled)
|
|
||||||
console.log(result.filePaths)
|
|
||||||
if (result.canceled || !result.filePaths[0]) return;
|
|
||||||
let soonData = fs.readFileSync(result.filePaths[0])
|
|
||||||
let j = JSON.parse(soonData.toString());
|
|
||||||
let type = j.soonType ? j.soonType : j.backBlackPic ? 2 : 1;
|
|
||||||
ipcRenderer.send('open-design-page', result.filePaths[0], type);
|
|
||||||
//console.log(result.filePaths)
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
Example:
|
||||||
|
nvm install v0.10.32 Install a specific version number
|
||||||
|
nvm use 0.10 Use the latest available 0.10.x release
|
||||||
|
nvm run 0.10.32 app.js Run app.js using node v0.10.32
|
||||||
|
nvm exec 0.10.32 node app.js Run `node app.js` with the PATH pointing to node v0.10.32
|
||||||
|
nvm alias default 0.10.32 Set default node version on a shell
|
||||||
|
|
||||||
|
Note:
|
||||||
|
to remove, delete, or uninstall nvm - just remove the `$NVM_DIR` folder (usually `~/.nvm`)
|
||||||
|
|
||||||
|
Xupnei:~ xuwenwei$ nvm install v16.5.0
|
||||||
|
|
||||||
|
Version 'v16.5.0' not found - try `nvm ls-remote` to browse available versions.
|
||||||
|
Xupnei:~ xuwenwei$
|
||||||
|
Xupnei:~ xuwenwei$ nvm install v16.5.0
|
||||||
|
Downloading and installing node v16.5.0...
|
||||||
|
Downloading https://nodejs.org/dist/v16.5.0/node-v16.5.0-darwin-arm64.tar.gz...
|
||||||
|
######################################################################### 100.0%
|
||||||
|
Computing checksum with shasum -a 256
|
||||||
|
Checksums matched!
|
||||||
|
Now using node v16.5.0 (npm v7.19.1)
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const { dialog } = require('@electron/remote');
|
||||||
|
|
||||||
|
// 辅助函数:获取文件名 (保留扩展名)
|
||||||
|
function get_filename(filePath) {
|
||||||
|
if (!filePath) return "";
|
||||||
|
return filePath.split(/[/\\]/).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let s_lan = "zh";
|
||||||
|
|
||||||
|
function language_str(str) {
|
||||||
|
let t = {
|
||||||
|
"noFile": { zh: "当前文件不存在!", ozh: "This file does not exist!", en: "File not found!" },
|
||||||
|
"saveTime": { zh: "保存时间:", ozh: "保存時間:", en: "Saved on:" },
|
||||||
|
"selectFile": { zh: "请选择文件", ozh: "請選擇文件", en: "Please select a file" },
|
||||||
|
"comfirm": { zh: "确认", ozh: "確認", en: "Confirm" },
|
||||||
|
"cancel": { zh: "取消", ozh: "取消", en: "Cancel" },
|
||||||
|
"SDFile": { zh: "SoonDesign模板文件", ozh: "SoonDesign模闆文件", en: "SoonDesign Template File" },
|
||||||
|
"vers": { zh: "版本:", ozh: "版本:", en: "Version:" },
|
||||||
|
"copyright": { zh: "版权所有 © 2023", ozh: "版權所有 © 2023", en: "Copyright © 2023" },
|
||||||
|
"about": { zh: "关于 SoonDesign", ozh: "關於 SoonDesign", en: "About SoonDesign" },
|
||||||
|
"lost": { zh: "(文件已丢失)", ozh: "(文件已丟失)", en: "(File Lost)" },
|
||||||
|
"clickToDelete": { zh: "文件已丢失,点击删除此记录", ozh: "文件已丟失,點擊刪除此記錄", en: "File lost, click to remove" },
|
||||||
|
"deleted": { zh: "已删除", ozh: "已刪除", en: "Deleted" },
|
||||||
|
"whetherSave": { zh: "是否保存当前文件?", ozh: "是否保存當前文件?", en: "Save current file?" }, // 补全可能用到的翻译
|
||||||
|
"delTitle": { zh: "警告", ozh: "警告", en: "Warning" },
|
||||||
|
"delContent": { zh: "文件已丢失,确认是否删除?", ozh: "文件已丟失,確認是否刪除?", en: "File lost, confirm delete?" }
|
||||||
|
}
|
||||||
|
return t[str][s_lan] || t[str]['zh'];
|
||||||
|
}
|
||||||
|
|
||||||
|
layui.use(['layer', 'form', 'jquery'], function () {
|
||||||
|
var $ = layui.$;
|
||||||
|
var layer = layui.layer
|
||||||
|
, form = layui.form;
|
||||||
|
|
||||||
|
if (window.sysAPI && window.sysAPI.getAppVersion) {
|
||||||
|
window.sysAPI.getAppVersion().then(ver => {
|
||||||
|
$("#app-version").text("v" + ver);
|
||||||
|
}).catch(e => console.log(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.send('get-sys-language');
|
||||||
|
|
||||||
|
ipcRenderer.on('close', (event, message) => {
|
||||||
|
ipcRenderer.send('run-close');
|
||||||
|
})
|
||||||
|
|
||||||
|
function langua_ge(lan = 'zh') {
|
||||||
|
$("[language='m']").each(function (i) {
|
||||||
|
$(this).html($(this).attr(lan));
|
||||||
|
})
|
||||||
|
$("[language='t']").each(function (i) {
|
||||||
|
$(this).attr("title", $(this).attr(lan));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#language_select').on('change', function () {
|
||||||
|
langua_ge($(this).find('option:selected').val());
|
||||||
|
s_lan = $(this).find('option:selected').val();
|
||||||
|
localStorage.setItem("lang", s_lan);
|
||||||
|
loadHistory();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('sys-lan', (event, data) => {
|
||||||
|
let lang = localStorage.getItem("lang");
|
||||||
|
if (lang) {
|
||||||
|
s_lan = lang;
|
||||||
|
} else {
|
||||||
|
if (data.startsWith('zh')) {
|
||||||
|
s_lan = data === 'zh-TW' ? 'ozh' : 'zh';
|
||||||
|
} else {
|
||||||
|
s_lan = 'en';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
langua_ge(s_lan);
|
||||||
|
$("#language_select").val(s_lan);
|
||||||
|
localStorage.setItem("lang", s_lan);
|
||||||
|
loadHistory();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 加载列表
|
||||||
|
// ==========================================
|
||||||
|
async function loadHistory() {
|
||||||
|
try {
|
||||||
|
const j = await window.sysAPI.readHistory();
|
||||||
|
let h = "";
|
||||||
|
let needUpdate = false;
|
||||||
|
|
||||||
|
for (let item of j.history) {
|
||||||
|
let src = "";
|
||||||
|
let fileExists = true;
|
||||||
|
let imgStyle = "";
|
||||||
|
let realType = item.type;
|
||||||
|
|
||||||
|
const soonData = await window.sysAPI.readJsonFile(item.path);
|
||||||
|
|
||||||
|
if (soonData) {
|
||||||
|
src = soonData.fp;
|
||||||
|
let fileType = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||||
|
if (item.type != fileType) {
|
||||||
|
item.type = fileType;
|
||||||
|
realType = fileType;
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileExists = false;
|
||||||
|
if (realType && (realType == 2 || realType == "2")) {
|
||||||
|
src = "./public/images/bg_2.png";
|
||||||
|
} else {
|
||||||
|
src = "./public/images/bg_1.png";
|
||||||
|
}
|
||||||
|
imgStyle = "opacity: 0.6; filter: grayscale(100%);";
|
||||||
|
}
|
||||||
|
|
||||||
|
let displayName = get_filename(item.path);
|
||||||
|
let cardTitle = item.path;
|
||||||
|
let cardStyle = "";
|
||||||
|
|
||||||
|
if (!fileExists) {
|
||||||
|
displayName += ` <span style='color:#ff5722;font-size:12px;'>${language_str("lost")}</span>`;
|
||||||
|
cardTitle = language_str("clickToDelete");
|
||||||
|
cardStyle = "border: 1px dashed #ff5722;";
|
||||||
|
}
|
||||||
|
|
||||||
|
h += `<div class="card" data="${item.path}" title="${cardTitle}" style="${cardStyle}">
|
||||||
|
<div class="rect ${realType == 2 ? 'rect1' : ''}">
|
||||||
|
<img src="${src}" style="width:190px; ${imgStyle}">
|
||||||
|
<div class="tip">${displayName}</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".card-list").html(h);
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
await window.sysAPI.writeHistory({ history: j.history });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("加载历史记录出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮事件
|
||||||
|
$("#openfile").click(function () { OpenDialog(); });
|
||||||
|
$("#new1").click(function () {
|
||||||
|
// 这里如果你也想改成和保存一样的弹窗逻辑,可以在这里加
|
||||||
|
ipcRenderer.send('open-design-page', "", 1);
|
||||||
|
});
|
||||||
|
$("#new2").click(function () { ipcRenderer.send('open-design-page', "", 2); });
|
||||||
|
|
||||||
|
$("#about").click(async function () {
|
||||||
|
let version = "3.0.0";
|
||||||
|
try {
|
||||||
|
if (window.sysAPI && window.sysAPI.getAppVersion) {
|
||||||
|
const v = await window.sysAPI.getAppVersion();
|
||||||
|
version = "v" + v;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
layer.open({
|
||||||
|
type: 1
|
||||||
|
, title: language_str("about")
|
||||||
|
, area: '450px;'
|
||||||
|
, id: 'LAY_layuipro'
|
||||||
|
, moveType: 1
|
||||||
|
, content: '<div style="padding-top:20px;padding-bottom:30px;background-color: #32363A; color: #fff; font-weight: 300;text-align:center;">' +
|
||||||
|
'<div style="width: 120px;margin: 0 auto;"><img style="width: 120px;" src="./public/images/IconA.png"></div>' +
|
||||||
|
'<div style="font-size:24px;">Soon Design</div>' +
|
||||||
|
'<div style="font-size:14px;">' + language_str("vers") + ' ' + version + '</div>' +
|
||||||
|
'<div style="font-size:14px;">' + language_str("copyright") + '</div>' +
|
||||||
|
'</div>'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 点击事件 (修改后:完全匹配“保存”弹窗样式)
|
||||||
|
// ==========================================
|
||||||
|
$(".card-list").on("click", '.card', async function () {
|
||||||
|
let filePath = $(this).attr("data");
|
||||||
|
|
||||||
|
// 再次检查文件是否存在
|
||||||
|
const soonData = await window.sysAPI.readJsonFile(filePath);
|
||||||
|
|
||||||
|
if (soonData) {
|
||||||
|
// 1. 文件存在 -> 正常打开
|
||||||
|
let type = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||||
|
|
||||||
|
const currentData = await window.sysAPI.readHistory();
|
||||||
|
let newHistory = currentData.history.filter(item => item.path !== filePath);
|
||||||
|
newHistory.unshift({ path: filePath, time: Date.now(), type: type });
|
||||||
|
await window.sysAPI.writeHistory({ history: newHistory });
|
||||||
|
|
||||||
|
ipcRenderer.send('open-design-page', filePath, type);
|
||||||
|
} else {
|
||||||
|
// 2. 文件不存在 -> 警告确认
|
||||||
|
|
||||||
|
// 【关键修改】
|
||||||
|
// - 纯文字,左对齐 (text-align: left)
|
||||||
|
// - 适当的 padding
|
||||||
|
// - 去除所有图标
|
||||||
|
let contentHtml = `
|
||||||
|
<div style="padding: 20px 20px; text-align: left; color: #333; font-size: 14px;">
|
||||||
|
${language_str("delContent")}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 使用 layer.open
|
||||||
|
layer.open({
|
||||||
|
type: 1,
|
||||||
|
title: language_str("delTitle"), // 标题:警告
|
||||||
|
content: contentHtml,
|
||||||
|
btn: [language_str("comfirm"), language_str("cancel")],
|
||||||
|
btnAlign: 'r', // 【关键修改】按钮右对齐
|
||||||
|
area: ['300px', 'auto'], // 【关键修改】宽度设为 300px,标准小窗口
|
||||||
|
resize: false,
|
||||||
|
shadeClose: true,
|
||||||
|
yes: async function(index){
|
||||||
|
const currentData = await window.sysAPI.readHistory();
|
||||||
|
const newHistory = currentData.history.filter(item => item.path !== filePath);
|
||||||
|
|
||||||
|
if (newHistory.length !== currentData.history.length) {
|
||||||
|
await window.sysAPI.writeHistory({ history: newHistory });
|
||||||
|
layer.msg(language_str("deleted"), { icon: 1, time: 1000 });
|
||||||
|
loadHistory();
|
||||||
|
}
|
||||||
|
layer.close(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function OpenDialog() {
|
||||||
|
dialog.showOpenDialog({
|
||||||
|
title: language_str("selectFile"),
|
||||||
|
buttonLabel: language_str("comfirm"),
|
||||||
|
filters: [
|
||||||
|
{ name: language_str("SDFile"), extensions: ['soon'] },
|
||||||
|
]
|
||||||
|
}).then(async (result) => {
|
||||||
|
if (result.canceled || !result.filePaths[0]) return;
|
||||||
|
|
||||||
|
let filePath = result.filePaths[0];
|
||||||
|
|
||||||
|
const soonData = await window.sysAPI.readJsonFile(filePath);
|
||||||
|
if (!soonData) {
|
||||||
|
layer.msg(language_str("noFile"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||||
|
|
||||||
|
const currentData = await window.sysAPI.readHistory();
|
||||||
|
let newHistory = currentData.history.filter(item => item.path !== filePath);
|
||||||
|
newHistory.unshift({ path: filePath, time: Date.now(), type: type });
|
||||||
|
|
||||||
|
await window.sysAPI.writeHistory({ history: newHistory });
|
||||||
|
|
||||||
|
ipcRenderer.send('open-design-page', filePath, type);
|
||||||
|
loadHistory();
|
||||||
|
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,11 +1,21 @@
|
|||||||
const { Menu, app, BrowserWindow, dialog } = require("electron");
|
const { ipcMain, Menu, shell, app, BrowserWindow, dialog } = require("electron");
|
||||||
let fs = require('fs')
|
const fs = require('fs');
|
||||||
const fontList = require("font-list");
|
const fontList = require("font-list");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const child_process = require("child_process");
|
const child_process = require("child_process");
|
||||||
|
|
||||||
console.log(`Node.js: ${process.versions.node}`);
|
console.log(`Node.js: ${process.versions.node}`);
|
||||||
|
|
||||||
|
// 初始化 Remote 模块
|
||||||
require("@electron/remote/main").initialize();
|
require("@electron/remote/main").initialize();
|
||||||
|
|
||||||
let preFilePath = "";
|
let preFilePath = "";
|
||||||
|
|
||||||
|
// 1. 定义数据路径(放在全局,方便后续调用)
|
||||||
|
const exePath = app.getPath('userData');
|
||||||
|
const historyFilePath = path.join(exePath, 'data.json');
|
||||||
|
console.log('User data path:', exePath);
|
||||||
|
|
||||||
app.on("will-finish-launching", () => {
|
app.on("will-finish-launching", () => {
|
||||||
app.on("open-file", (e, filePath) => {
|
app.on("open-file", (e, filePath) => {
|
||||||
preFilePath = filePath;
|
preFilePath = filePath;
|
||||||
@@ -16,15 +26,13 @@ app.on("will-finish-launching", () => {
|
|||||||
preFilePath = process.argv[1] === "." ? "" : process.argv[1];
|
preFilePath = process.argv[1] === "." ? "" : process.argv[1];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 使用用户数据目录,避免管理员权限问题
|
|
||||||
const exePath = app.getPath('userData')
|
|
||||||
console.log('User data path:', exePath);
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
let size = require("electron").screen.getPrimaryDisplay().workAreaSize;
|
let size = require("electron").screen.getPrimaryDisplay().workAreaSize;
|
||||||
//console.log(require('electron').screen.getPrimaryDisplay());
|
|
||||||
let width = parseInt(size.width);
|
let width = parseInt(size.width);
|
||||||
var mainWindow;
|
var mainWindow;
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: size.width,
|
width: size.width,
|
||||||
@@ -32,25 +40,31 @@ function createWindow() {
|
|||||||
resizable: false,
|
resizable: false,
|
||||||
icon: "public/images/favicon.ico", // sets window icon
|
icon: "public/images/favicon.ico", // sets window icon
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, "preload.js"),
|
preload: path.join(__dirname, "preload.js"), // 确保你有这个文件
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false, // 注意:为了安全建议后续开启,但目前保持你的配置
|
||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.maximize();
|
mainWindow.maximize();
|
||||||
// Menu.setApplicationMenu(null);//关闭菜单
|
//Menu.setApplicationMenu(null); // 关闭菜单
|
||||||
// and load the index.html of the app.
|
|
||||||
//mainWindow.maximize();
|
// 启动逻辑:如果有预打开的文件,直接跳转
|
||||||
if (preFilePath) {
|
if (preFilePath) {
|
||||||
|
try {
|
||||||
let soonData = fs.readFileSync(preFilePath)
|
let soonData = fs.readFileSync(preFilePath)
|
||||||
let j = JSON.parse(soonData.toString());
|
let j = JSON.parse(soonData.toString());
|
||||||
let type = j.soonType ? j.soonType : j.backBlackPic ? 2 : 1;
|
let type = j.soonType ? j.soonType : j.backBlackPic ? 2 : 1;
|
||||||
mainWindow.loadURL(`file://${__dirname}/design${type}.html?file=${preFilePath}&type=${type}`);
|
mainWindow.loadURL(`file://${__dirname}/design${type}.html?file=${preFilePath}&type=${type}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error loading preFilePath:", e);
|
||||||
|
mainWindow.loadFile("index.html");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadFile("index.html");
|
mainWindow.loadFile("index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
var close_flag = 0;
|
var close_flag = 0;
|
||||||
mainWindow.on("close", function (e) {
|
mainWindow.on("close", function (e) {
|
||||||
if (close_flag == 0) {
|
if (close_flag == 0) {
|
||||||
@@ -59,59 +73,132 @@ function createWindow() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
// mainWindow.webContents.openDevTools({mode:'bottom'});
|
|
||||||
//1150 width 720 height
|
// ==========================================
|
||||||
// Open the DevTools.
|
// 新增:安全的文件读写处理 (IPC Handle)
|
||||||
//mainWindow.webContents.openDevTools()
|
// ==========================================
|
||||||
const { ipcMain } = require("electron");
|
|
||||||
|
// 1. 读取历史记录
|
||||||
|
ipcMain.handle('history:read', async () => {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(historyFilePath)) {
|
||||||
|
return { history: [] };
|
||||||
|
}
|
||||||
|
const data = fs.readFileSync(historyFilePath, 'utf-8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('主进程读取历史失败:', err);
|
||||||
|
return { history: [] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 写入历史记录 (包含权限检查)
|
||||||
|
ipcMain.handle('history:write', async (event, data) => {
|
||||||
|
try {
|
||||||
|
// 简单的权限检查
|
||||||
|
if (fs.existsSync(historyFilePath)) {
|
||||||
|
try {
|
||||||
|
fs.accessSync(historyFilePath, fs.constants.R_OK | fs.constants.W_OK);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("无权限写入配置文件");
|
||||||
|
return { success: false, error: "PERMISSION_DENIED" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs.writeFileSync(historyFilePath, JSON.stringify(data));
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('主进程写入历史失败:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 新增:获取软件版本号
|
||||||
|
ipcMain.handle('app:get-version', () => {
|
||||||
|
return app.getVersion(); // 自动读取 package.json 中的 version 字段
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. 读取 .soon 项目文件 (用于获取缩略图等)
|
||||||
|
ipcMain.handle('file:read-json', async (event, filePath) => {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(filePath)) return null;
|
||||||
|
const data = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
} catch (err) {
|
||||||
|
// console.error('读取文件失败:', filePath, err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 原有的 IPC 监听
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
ipcMain.on("get-sys-fonts", (event) => {
|
ipcMain.on("get-sys-fonts", (event) => {
|
||||||
fontList
|
fontList.getFonts()
|
||||||
.getFonts()
|
|
||||||
.then((fonts) => {
|
.then((fonts) => {
|
||||||
event.reply("font-list", fonts);
|
event.reply("font-list", fonts);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
//console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("get-sys-language", (event) => {
|
ipcMain.on("get-sys-language", (event) => {
|
||||||
event.reply("sys-lan", app.getLocale());
|
event.reply("sys-lan", app.getLocale());
|
||||||
});
|
});
|
||||||
ipcMain.on("open-help-file", (event) => {
|
|
||||||
// help 文件在应用目录,不在用户数据目录
|
// 修复后的 Help 文件打开逻辑
|
||||||
let helpPath = !app.isPackaged
|
ipcMain.on("open-help-file", async (event) => {
|
||||||
? path.join(process.cwd(), 'help', 'User Manual.pdf')
|
let helpPath;
|
||||||
: path.join(path.dirname(process.execPath), 'resources', 'help', 'User Manual.pdf')
|
const fileName = 'User Manual.pdf';
|
||||||
child_process.exec(`start "" "${helpPath}"`);
|
|
||||||
|
if (app.isPackaged) {
|
||||||
|
helpPath = path.join(process.resourcesPath, 'help', fileName);
|
||||||
|
} else {
|
||||||
|
helpPath = path.join(process.cwd(), 'help', fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Help file path: ", helpPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(helpPath)) {
|
||||||
|
try {
|
||||||
|
const errorMessage = await shell.openPath(helpPath);
|
||||||
|
if (errorMessage) {
|
||||||
|
console.error('Error opening help file:', errorMessage);
|
||||||
|
} else {
|
||||||
|
console.log('Help file opened successfully');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception when opening file:', err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('Help file not found at:', helpPath);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听从渲染进程发送来的请求
|
|
||||||
ipcMain.on("print-pdf", (event, pdfData) => {
|
ipcMain.on("print-pdf", (event, pdfData) => {
|
||||||
// 打印 PDF 数据
|
|
||||||
printPDF(pdfData);
|
printPDF(pdfData);
|
||||||
});
|
});
|
||||||
|
|
||||||
function printPDF(pdfData) {
|
function printPDF(pdfData) {
|
||||||
// 使用 Electron 的打印对话框或默认打印机进行打印
|
const printWindow = new BrowserWindow({ show: true }); // 如果不想显示窗口,改 show: false
|
||||||
// 创建一个隐藏的webview以加载PDF数据并进行打印
|
|
||||||
const printWindow = new BrowserWindow({ show: true });
|
|
||||||
printWindow.loadURL(pdfData);
|
printWindow.loadURL(pdfData);
|
||||||
|
|
||||||
printWindow.webContents.on("did-finish-load", () => {
|
printWindow.webContents.on("did-finish-load", () => {
|
||||||
// 使用默认打印机打印PDF
|
|
||||||
printWindow.webContents.print({});
|
printWindow.webContents.print({});
|
||||||
console.log("123")
|
// printWindow.close(); // 打印通常是异步的,直接关闭可能导致打印失败
|
||||||
//printWindow.close();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on("open-design-page", (event, arg, type) => {
|
ipcMain.on("open-design-page", (event, arg, type) => {
|
||||||
arg = arg ? arg : 'empty'
|
arg = arg ? arg : 'empty'
|
||||||
type = type ? type : '1'
|
type = type ? type : '1'
|
||||||
mainWindow.loadURL(`file://${__dirname}/design${type}.html?file=${arg}&type=${type}`);
|
mainWindow.loadURL(`file://${__dirname}/design${type}.html?file=${arg}&type=${type}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("open-first-page", (event, arg) => {
|
ipcMain.on("open-first-page", (event, arg) => {
|
||||||
mainWindow.loadURL(`file://${__dirname}/index.html`);
|
mainWindow.loadURL(`file://${__dirname}/index.html`);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("run-close", (event, arg) => {
|
ipcMain.on("run-close", (event, arg) => {
|
||||||
close_flag = 1;
|
close_flag = 1;
|
||||||
app.quit();
|
app.quit();
|
||||||
@@ -120,26 +207,18 @@ function createWindow() {
|
|||||||
require("@electron/remote/main").enable(mainWindow.webContents);
|
require("@electron/remote/main").enable(mainWindow.webContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
|
||||||
// initialization and is ready to create browser windows.
|
|
||||||
// Some APIs can only be used after this event occurs.
|
|
||||||
app.commandLine.appendSwitch("no-sandbox");
|
app.commandLine.appendSwitch("no-sandbox");
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
createWindow();
|
createWindow();
|
||||||
require("@electron/remote/main").initialize();
|
// initialize 已经在顶部调用过,这里不需要重复调用,但保留也无害
|
||||||
|
// require("@electron/remote/main").initialize();
|
||||||
|
|
||||||
app.on("activate", function () {
|
app.on("activate", function () {
|
||||||
// On macOS it's common to re-create a window in the app when the
|
|
||||||
// dock icon is clicked and there are no other windows open.
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quit when all windows are closed, except on macOS. There, it's common
|
|
||||||
// for applications and their menu bar to stay active until the user quits
|
|
||||||
// explicitly with Cmd + Q.
|
|
||||||
app.on("window-all-closed", function () {
|
app.on("window-all-closed", function () {
|
||||||
if (process.platform !== "darwin") app.quit();
|
if (process.platform !== "darwin") app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// In this file you can include the rest of your app's specific main process
|
|
||||||
// code. You can also put them in separate files and require them here.
|
|
||||||
|
|||||||
@@ -1,104 +1,57 @@
|
|||||||
{
|
{
|
||||||
"name": "SoonDesign",
|
"name": "SoonDesign",
|
||||||
"version": "2.1.248",
|
"version": "2.3.017",
|
||||||
"description": "tools of design soon files",
|
"description": "tools of design soon files",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
|
"author": "Jerry <jerry@cardsoon.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/cello847/soondesign"
|
||||||
|
},
|
||||||
|
"homepage": "https://soondesign.cardsoon.com",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/cello847/soondesign/issues"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "electron-forge start",
|
||||||
"build": "electron-builder",
|
"package:x64": "electron-forge package --arch=x64",
|
||||||
"build:win": "electron-builder --win --x64",
|
"package:arm64": "electron-forge package --arch=arm64",
|
||||||
"build:mac": "electron-builder --mac --x64",
|
"package:universal": "electron-forge package --arch=universal"
|
||||||
"build:linux": "electron-builder --linux --x64",
|
|
||||||
"build:all": "electron-builder -mwl"
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"productName": "SoonDesign",
|
|
||||||
"appId": "com.SoonDesign",
|
|
||||||
"copyright": "",
|
|
||||||
"directories": {
|
|
||||||
"output": "build"
|
|
||||||
},
|
|
||||||
"extraFiles": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"nsis": {
|
|
||||||
"oneClick": false,
|
|
||||||
"allowElevation": true,
|
|
||||||
"allowToChangeInstallationDirectory": true,
|
|
||||||
"installerIcon": "public/images/favicon.ico",
|
|
||||||
"uninstallerIcon": "public/images/favicon.ico",
|
|
||||||
"installerHeaderIcon": "public/images/favicon.ico",
|
|
||||||
"createDesktopShortcut": true,
|
|
||||||
"createStartMenuShortcut": true
|
|
||||||
},
|
|
||||||
"fileAssociations": [
|
|
||||||
{
|
|
||||||
"name": "SoonDesign file",
|
|
||||||
"ext": "soon",
|
|
||||||
"icon": "public/images/favicon.ico",
|
|
||||||
"description": "SoonDesign file associations"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"asar": true,
|
|
||||||
"asarUnpack": [
|
|
||||||
"lib/**"
|
|
||||||
],
|
|
||||||
"win": {
|
|
||||||
"icon": "public/images/favicon.ico",
|
|
||||||
"requestedExecutionLevel": "asInvoker",
|
|
||||||
"target": [
|
|
||||||
{
|
|
||||||
"target": "nsis",
|
|
||||||
"arch": [
|
|
||||||
"x64"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mac": {
|
|
||||||
"target": [
|
|
||||||
{ "target": "dmg", "arch": ["x64"] },
|
|
||||||
{ "target": "zip", "arch": ["x64"] }
|
|
||||||
],
|
|
||||||
"category": "public.app-category.utilities",
|
|
||||||
"icon": "public/images/favicon.icns"
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"target": [
|
|
||||||
{ "target": "AppImage", "arch": ["x64"] },
|
|
||||||
{ "target": "deb", "arch": ["x64"] }
|
|
||||||
],
|
|
||||||
"category": "Utility",
|
|
||||||
"icon": "public/images/IconA256.png",
|
|
||||||
"desktop": {
|
|
||||||
"Name": "SoonDesign",
|
|
||||||
"Comment": "tools of design soon files"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"electronPackagerConfig": {
|
"electronPackagerConfig": {
|
||||||
"packageManager": "npm",
|
"packageManager": "npm",
|
||||||
"icon": "./public/images/favicon.ico"
|
"icon": "./public/images/favicon.icns"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"forge": {
|
||||||
|
"packagerConfig": {
|
||||||
|
"icon": "./public/images/favicon.icns",
|
||||||
|
"asar": true,
|
||||||
|
"extraResource": [
|
||||||
|
"./help"
|
||||||
|
],
|
||||||
|
"osxUniversal": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"repository": "",
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Electron",
|
"SoonDesign",
|
||||||
"quick",
|
"soon",
|
||||||
"start",
|
"start",
|
||||||
"tutorial",
|
"tutorial",
|
||||||
"demo"
|
"demo"
|
||||||
],
|
],
|
||||||
"author": "",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^25.0.0",
|
"@electron-forge/cli": "^6.2.0",
|
||||||
"electron-builder": "^23.3.3"
|
"electron": "^25.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.0.8",
|
"@electron/remote": "^2.0.8",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"bytenode": "^1.3.4",
|
"bytenode": "^1.3.4",
|
||||||
|
"create-dmg": "^7.0.0",
|
||||||
"electron-squirrel-startup": "^1.0.1",
|
"electron-squirrel-startup": "^1.0.1",
|
||||||
"font-list": "^1.4.2",
|
"font-list": "^1.4.2",
|
||||||
"jr-qrcode": "^1.1.4",
|
"jr-qrcode": "^1.1.4",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// All of the Node.js APIs are available in the preload process.
|
const { ipcRenderer } = require('electron')
|
||||||
// It has the same sandbox as a Chrome extension.
|
|
||||||
|
// 1. 显示版本号逻辑 (保持不变)
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
const replaceText = (selector, text) => {
|
const replaceText = (selector, text) => {
|
||||||
const element = document.getElementById(selector)
|
const element = document.getElementById(selector)
|
||||||
@@ -10,3 +11,19 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
replaceText(`${type}-version`, process.versions[type])
|
replaceText(`${type}-version`, process.versions[type])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 2. 修正:直接挂载到 window 对象
|
||||||
|
// 因为你的 main.js 中 contextIsolation: false,所以直接赋值即可
|
||||||
|
window.sysAPI = {
|
||||||
|
// 读取历史记录
|
||||||
|
readHistory: () => ipcRenderer.invoke('history:read'),
|
||||||
|
|
||||||
|
// 写入历史记录
|
||||||
|
writeHistory: (data) => ipcRenderer.invoke('history:write', data),
|
||||||
|
|
||||||
|
// 读取任意 JSON 文件
|
||||||
|
readJsonFile: (filePath) => ipcRenderer.invoke('file:read-json', filePath),
|
||||||
|
//获取软件版本
|
||||||
|
getAppVersion: () => ipcRenderer.invoke('app:get-version')
|
||||||
|
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,219 @@
|
|||||||
|
function initAligningGuidelines(canvas) {
|
||||||
|
|
||||||
|
var ctx = canvas.getSelectionContext(),
|
||||||
|
//aligningLineOffset = 0,
|
||||||
|
//aligningLineMargin = 0,
|
||||||
|
//aligningLineWidth = 1,
|
||||||
|
//aligningLineColor = 'rgb(0,0,0)',
|
||||||
|
aligningLineOffset = 4,
|
||||||
|
aligningLineMargin = 5,//吸引
|
||||||
|
aligningLineWidth = 6,
|
||||||
|
aligningLineColor = '#6637FF',
|
||||||
|
viewportTransform,
|
||||||
|
zoom = 1;
|
||||||
|
|
||||||
|
function drawVerticalLine(coords) {
|
||||||
|
drawLine(
|
||||||
|
coords.x + 0.5,
|
||||||
|
coords.y1 > coords.y2 ? coords.y2 : coords.y1,
|
||||||
|
coords.x + 0.5,
|
||||||
|
coords.y2 > coords.y1 ? coords.y2 : coords.y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHorizontalLine(coords) {
|
||||||
|
drawLine(
|
||||||
|
coords.x1 > coords.x2 ? coords.x2 : coords.x1,
|
||||||
|
coords.y + 0.5,
|
||||||
|
coords.x2 > coords.x1 ? coords.x2 : coords.x1,
|
||||||
|
coords.y + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLine(x1, y1, x2, y2) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.lineWidth = aligningLineWidth;
|
||||||
|
ctx.strokeStyle = aligningLineColor;
|
||||||
|
ctx.beginPath();
|
||||||
|
let width = canvas.getWidth();
|
||||||
|
let height = canvas.getHeight();
|
||||||
|
ctx.moveTo(width / 2 - ( ( width / 2 - (x1) ) * zoom ) , height / 2 - ( ( height / 2 - (y1) ) * zoom ));//哪个点散射出来
|
||||||
|
ctx.lineTo(width / 2 - ( ( width / 2 - (x2) ) * zoom ), height / 2 - ( ( height / 2 - (y2) ) * zoom ));//结束到哪个点
|
||||||
|
//修复中心放大后的位置偏移 2021-10-24
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInRange(value1, value2) {
|
||||||
|
value1 = Math.round(value1);
|
||||||
|
value2 = Math.round(value2);
|
||||||
|
for (var i = value1 - aligningLineMargin, len = value1 + aligningLineMargin; i <= len; i++) {
|
||||||
|
if (i === value2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var verticalLines = [],
|
||||||
|
horizontalLines = [];
|
||||||
|
|
||||||
|
canvas.on('mouse:down', function () {
|
||||||
|
viewportTransform = canvas.viewportTransform;
|
||||||
|
zoom = canvas.getZoom();
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.on('object:moving', function(e) {
|
||||||
|
|
||||||
|
var activeObject = e.target,
|
||||||
|
canvasObjects = canvas.getObjects(),
|
||||||
|
activeObjectCenter = activeObject.getCenterPoint(),
|
||||||
|
activeObjectLeft = activeObjectCenter.x,
|
||||||
|
activeObjectTop = activeObjectCenter.y,
|
||||||
|
activeObjectBoundingRect = activeObject.getBoundingRect(),
|
||||||
|
activeObjectHeight = activeObjectBoundingRect.height / viewportTransform[3],
|
||||||
|
activeObjectWidth = activeObjectBoundingRect.width / viewportTransform[0],
|
||||||
|
horizontalInTheRange = false,
|
||||||
|
verticalInTheRange = false,
|
||||||
|
transform = canvas._currentTransform;
|
||||||
|
|
||||||
|
if (!transform) return;
|
||||||
|
|
||||||
|
// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions,
|
||||||
|
// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move
|
||||||
|
|
||||||
|
for (var i = canvasObjects.length; i--; ) {
|
||||||
|
|
||||||
|
if (canvasObjects[i] === activeObject) continue;
|
||||||
|
|
||||||
|
var objectCenter = canvasObjects[i].getCenterPoint(),
|
||||||
|
objectLeft = objectCenter.x,
|
||||||
|
objectTop = objectCenter.y,
|
||||||
|
objectBoundingRect = canvasObjects[i].getBoundingRect(),
|
||||||
|
objectHeight = objectBoundingRect.height / viewportTransform[3],
|
||||||
|
objectWidth = objectBoundingRect.width / viewportTransform[0];
|
||||||
|
|
||||||
|
// snap by the horizontal center line
|
||||||
|
if (isInRange(objectLeft, activeObjectLeft)) {
|
||||||
|
verticalInTheRange = true;
|
||||||
|
verticalLines.push({
|
||||||
|
x: objectLeft,
|
||||||
|
y1: (objectTop < activeObjectTop)
|
||||||
|
? (objectTop - objectHeight / 2 - aligningLineOffset)
|
||||||
|
: (objectTop + objectHeight / 2 + aligningLineOffset),
|
||||||
|
y2: (activeObjectTop > objectTop)
|
||||||
|
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(objectLeft, activeObjectTop), 'center', 'center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// snap by the left edge
|
||||||
|
if (isInRange(objectLeft - objectWidth / 2, activeObjectLeft - activeObjectWidth / 2)) {
|
||||||
|
verticalInTheRange = true;
|
||||||
|
verticalLines.push({
|
||||||
|
x: objectLeft - objectWidth / 2,
|
||||||
|
y1: (objectTop < activeObjectTop)
|
||||||
|
? (objectTop - objectHeight / 2 - aligningLineOffset)
|
||||||
|
: (objectTop + objectHeight / 2 + aligningLineOffset),
|
||||||
|
y2: (activeObjectTop > objectTop)
|
||||||
|
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(objectLeft - objectWidth / 2 + activeObjectWidth / 2, activeObjectTop), 'center', 'center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// snap by the right edge
|
||||||
|
if (isInRange(objectLeft + objectWidth / 2, activeObjectLeft + activeObjectWidth / 2)) {
|
||||||
|
verticalInTheRange = true;
|
||||||
|
verticalLines.push({
|
||||||
|
x: objectLeft + objectWidth / 2,
|
||||||
|
y1: (objectTop < activeObjectTop)
|
||||||
|
? (objectTop - objectHeight / 2 - aligningLineOffset)
|
||||||
|
: (objectTop + objectHeight / 2 + aligningLineOffset),
|
||||||
|
y2: (activeObjectTop > objectTop)
|
||||||
|
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(objectLeft + objectWidth / 2 - activeObjectWidth / 2, activeObjectTop), 'center', 'center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// snap by the vertical center line
|
||||||
|
if (isInRange(objectTop, activeObjectTop)) {
|
||||||
|
horizontalInTheRange = true;
|
||||||
|
horizontalLines.push({
|
||||||
|
y: objectTop,
|
||||||
|
x1: (objectLeft < activeObjectLeft)
|
||||||
|
? (objectLeft - objectWidth / 2 - aligningLineOffset)
|
||||||
|
: (objectLeft + objectWidth / 2 + aligningLineOffset),
|
||||||
|
x2: (activeObjectLeft > objectLeft)
|
||||||
|
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop), 'center', 'center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// snap by the top edge
|
||||||
|
if (isInRange(objectTop - objectHeight / 2, activeObjectTop - activeObjectHeight / 2)) {
|
||||||
|
horizontalInTheRange = true;
|
||||||
|
horizontalLines.push({
|
||||||
|
y: objectTop - objectHeight / 2,
|
||||||
|
x1: (objectLeft < activeObjectLeft)
|
||||||
|
? (objectLeft - objectWidth / 2 - aligningLineOffset)
|
||||||
|
: (objectLeft + objectWidth / 2 + aligningLineOffset),
|
||||||
|
x2: (activeObjectLeft > objectLeft)
|
||||||
|
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop - objectHeight / 2 + activeObjectHeight / 2), 'center', 'center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// snap by the bottom edge
|
||||||
|
if (isInRange(objectTop + objectHeight / 2, activeObjectTop + activeObjectHeight / 2)) {
|
||||||
|
horizontalInTheRange = true;
|
||||||
|
horizontalLines.push({
|
||||||
|
y: objectTop + objectHeight / 2,
|
||||||
|
x1: (objectLeft < activeObjectLeft)
|
||||||
|
? (objectLeft - objectWidth / 2 - aligningLineOffset)
|
||||||
|
: (objectLeft + objectWidth / 2 + aligningLineOffset),
|
||||||
|
x2: (activeObjectLeft > objectLeft)
|
||||||
|
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
|
||||||
|
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
|
||||||
|
});
|
||||||
|
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop + objectHeight / 2 - activeObjectHeight / 2), 'center', 'center');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!horizontalInTheRange) {
|
||||||
|
horizontalLines.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verticalInTheRange) {
|
||||||
|
verticalLines.length = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.on('before:render', function() {
|
||||||
|
|
||||||
|
if(canvas&&canvas.contextTop)
|
||||||
|
{
|
||||||
|
canvas.clearContext(canvas.contextTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.on('after:render', function() {
|
||||||
|
for (var i = verticalLines.length; i--; ) {
|
||||||
|
drawVerticalLine(verticalLines[i]);
|
||||||
|
}
|
||||||
|
for (var i = horizontalLines.length; i--; ) {
|
||||||
|
drawHorizontalLine(horizontalLines[i]);
|
||||||
|
}
|
||||||
|
verticalLines.length = horizontalLines.length = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.on('mouse:up', function() {
|
||||||
|
verticalLines.length = horizontalLines.length = 0;
|
||||||
|
canvas.renderAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||