diff --git a/electron.vite.config.mjs b/electron.vite.config.mjs
index f5f2935..cac99ab 100644
--- a/electron.vite.config.mjs
+++ b/electron.vite.config.mjs
@@ -11,6 +11,9 @@ export default defineConfig({
plugins: [externalizeDepsPlugin()]
},
renderer: {
+ server: {
+ hmr: false,
+ },
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
diff --git a/src/renderer/src/App.jsx b/src/renderer/src/App.jsx
index 689c15a..065bf12 100644
--- a/src/renderer/src/App.jsx
+++ b/src/renderer/src/App.jsx
@@ -29,10 +29,12 @@ import {
} from './Imports'
import { createSignal } from 'solid-js'
import "./styles/index.css"
+import "./styles/animations.css"
import MainToolbar from './components/MainToolbar'
import MainToolbarSideContainer from './components/MainToolbarSideContainer'
import BottomSidebar from './components/BottomSidebar'
import ScreenSidebar from './components/ScreenSidebar'
+import PenOverlays from './overlays/PenOverlays'
function App() {
@@ -44,7 +46,7 @@ function App() {
const [toolbarItems, setToolbarItems] = createSignal([
{type: 'button', icon: iconCursor, solidIcon: iconSolidCursor, name: "鼠標模式", action: "mouse"},
- {type: 'button', icon: iconPen, solidIcon: iconSolidPen, name: "批註模式", action: "pen"},
+ {type: 'button', icon: iconPen, solidIcon: iconSolidPen, name: "批註模式", action: "pen", overlay: PenOverlays},
{type: 'button', icon: iconEraser, name: "橡皮擦模式", solidIcon:iconSolidEraser, action: "eraser"},
{type: 'button', icon: iconTrash, name: "清空畫布", action: "clear"},
{type: "separator"},
diff --git a/src/renderer/src/components/ToolBarButton.jsx b/src/renderer/src/components/ToolBarButton.jsx
index a9ad23d..1ce5afe 100644
--- a/src/renderer/src/components/ToolBarButton.jsx
+++ b/src/renderer/src/components/ToolBarButton.jsx
@@ -1,64 +1,130 @@
// noinspection ES6UnusedImports
import gest from "../utils/gest"
+import { createEffect, createSignal, on, Show, splitProps } from 'solid-js'
+import { Dynamic } from 'solid-js/web'
/**
* 定義單個工具欄按鈕的Solid組件
* @param props{{
* item: {
* name:string, icon, solidIcon,
- * type: "button" | "separator" | "pagination-indicator",
+ * type: "button" | "separator" | "pagination-indicator"| "pagination-indicator-sidebar",
* action: string,
* }, toolbarSignal, setToolbarSignal,
* }} 傳入工具欄按鈕的相關選項。
* */
export default function ToolBarButton(props) {
- const v = props.item;
- const toolbarSignal = props.toolbarSignal;
- const setToolbarSignal = props.setToolbarSignal;
+ let toolButtonRef;
+ const [boundingRect, updateBoundingRect] = createSignal({x:0,y:0});
+ const [isOverlayDisplay, setOverlayDisplay] = createSignal(false);
+ const [isAnimating, setAnimating] = createSignal(false);
+ const [className, setClassName] = createSignal({})
+ const [timerID, setTimerID] = createSignal(0);
+ const [{item:v,toolbarSignal,setToolbarSignal},_] = splitProps(props,["item","toolbarSignal","setToolbarSignal"])
+
+ const overlayVisibleFalseTimerFn = ()=>{
+ setOverlayDisplay(false)
+ setAnimating(false);
+ };
+
+ /**
+ * 用於animate Overlay的顯示與隱藏效果
+ * @param isVisible{boolean} 手動指定是要顯示還是隱藏Overlay,
+ * 未指定該param時,根據當前Signal切換狀態
+ * */
+ const changeOverlayVisibility = function(isVisible) {
+ // 防抖處理
+ if (isAnimating()===true) return 0;
+ // 移除現有的Timer
+ clearTimeout(timerID());
+ setTimerID(0);
+ // 清空className,移除所有animation
+ setClassName("")
+ // isVisible===true
+ if (isVisible!==undefined&&isVisible!==null&&isVisible===true) {
+ if (isOverlayDisplay()&&isOverlayDisplay()===true) return 0;
+ setAnimating(true);
+ setClassName("animate-fade-in");
+ setOverlayDisplay(true);
+ } else if (isVisible!==undefined&&isVisible!==null&&isVisible===false) {
+ if (isOverlayDisplay()&&isOverlayDisplay()===false) return 0;
+ setClassName("animate-fade-out");
+ setTimerID(setTimeout(overlayVisibleFalseTimerFn,90))
+ } else {
+ // no isVisible param
+ if (isOverlayDisplay()===true) {
+ // to false
+ setAnimating(true);
+ setClassName("animate-fade-out");
+ setTimerID(setTimeout(overlayVisibleFalseTimerFn,90))
+ } else if (isOverlayDisplay()===false) {
+ setClassName("animate-fade-in");
+ setOverlayDisplay(true);
+ }
+ }
+ }
+
+ createEffect(on(toolbarSignal,(tbsignal)=>{
+ if (tbsignal.activeTool!==v.action) changeOverlayVisibility(false)
+ }))
+
// noinspection HtmlUnknownAttribute
return (
-
{
- if (v.action === "drawer") {
- setToolbarSignal({
- ...toolbarSignal(),
- isDrawerOpen: !toolbarSignal().isDrawerOpen
- });
- } else if (!(["clear"].includes(v.action))&&v.action){
- setToolbarSignal({
- ...toolbarSignal(),
- activeTool: v.action,
- })
- }
- }}
- classList={{
- "toolbar-item": v.type !== "pagination-indicator"&&v.type!=="pagination-indicator-sidebar",
- "toolbar-item-separator": v.type === "separator",
- "toolbar-item-pagination-indicator": v.type === "pagination-indicator"||v.type==="pagination-indicator-sidebar",
- "toolbar-item-active": toolbarSignal().activeTool===v.action
- }} use:gest={null}>
- {v.type === "separator" ?
: v.type === 'button' ? (
- <>
- {v.action === "drawer" && toolbarSignal().isDrawerOpen === true ? (
-
- ) : <>
-
-
- >}
-
- >
- ) : v.type === "pagination-indicator" ? <>
-
- > : v.type === "pagination-indicator-sidebar" ? <>
-
- > : void 0}
-
+ <>
+ {
+ if (v.action === "drawer") {
+ setToolbarSignal({
+ ...toolbarSignal(),
+ isDrawerOpen: !toolbarSignal().isDrawerOpen
+ });
+ } else if (v.action&&v.action===toolbarSignal().activeTool&&v.overlay) {
+ // 傳入BoundingRect讓Overlay更新位置
+ updateBoundingRect(JSON.parse(JSON.stringify(toolButtonRef.getBoundingClientRect())))
+ // 切換Overlay的顯示
+ changeOverlayVisibility();
+ } else if (!(["clear"].includes(v.action))&&v.action){
+ setToolbarSignal({
+ ...toolbarSignal(),
+ activeTool: v.action,
+ })
+ }
+ }} ref={toolButtonRef}
+ classList={{
+ "toolbar-item": v.type !== "pagination-indicator"&&v.type!=="pagination-indicator-sidebar",
+ "toolbar-item-separator": v.type === "separator",
+ "toolbar-item-pagination-indicator": v.type === "pagination-indicator"||v.type==="pagination-indicator-sidebar",
+ "toolbar-item-active": toolbarSignal().activeTool===v.action
+ }} use:gest={null}>
+ {v.type === "separator" ?
: v.type === 'button' ? (
+ <>
+ {v.action === "drawer" && toolbarSignal().isDrawerOpen === true ? (
+
+ ) : <>
+
+
+ >}
+
+ >
+ ) : v.type === "pagination-indicator" ? <>
+
+ > : v.type === "pagination-indicator-sidebar" ? <>
+
+ > : void 0}
+
+
+
+
+ >
)
}
\ No newline at end of file
diff --git a/src/renderer/src/i18n/manifest.js b/src/renderer/src/i18n/manifest.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/renderer/src/overlays/PenOverlays.jsx b/src/renderer/src/overlays/PenOverlays.jsx
new file mode 100644
index 0000000..5bd63a3
--- /dev/null
+++ b/src/renderer/src/overlays/PenOverlays.jsx
@@ -0,0 +1,18 @@
+import { Portal } from 'solid-js/web'
+import { splitProps } from 'solid-js'
+
+export default function PenOverlays(props) {
+ const [{toolbarSignal,setToolbarSignal,boundingRect,animateClass},_] = splitProps(props,["toolbarSignal","setToolbarSignal","boundingRect","animateClass"]);
+ return (
+ <>
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/renderer/src/styles/animations.css b/src/renderer/src/styles/animations.css
new file mode 100644
index 0000000..130a45c
--- /dev/null
+++ b/src/renderer/src/styles/animations.css
@@ -0,0 +1,17 @@
+@keyframes fade-in {
+ from {opacity: 0}
+ to {opacity: 1}
+}
+@keyframes fade-out {
+ from {opacity: 1}
+ to {opacity: 0}
+}
+.animate-fade-in {
+ animation-name: fade-in !important;
+ animation-duration: 100ms !important;
+}
+.animate-fade-out {
+ animation-name: fade-out !important;
+ animation-duration: 100ms !important;
+ animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
+}
\ No newline at end of file
diff --git a/src/renderer/src/styles/index.css b/src/renderer/src/styles/index.css
index 4b8919e..f279050 100644
--- a/src/renderer/src/styles/index.css
+++ b/src/renderer/src/styles/index.css
@@ -13,10 +13,10 @@
user-select: none;
}
.zph-container.window-border {
- @apply ring-1 ring-inset ring-red-600 ;
+ @apply "ring-1 ring-inset ring-red-600";
}
.zph-container.window-black {
- background-color: black !important;
+ background-color: white !important;
}
/*Main Toolbar*/
@@ -31,7 +31,7 @@
align-items: center;
justify-content: center;
z-index: 50;
- @apply bottom-1;
+ @apply "bottom-1";
}
.main-toolbar-container>div {
flex-shrink: 0 !important;
@@ -39,17 +39,17 @@
pointer-events: auto;
height: 100vh;
position: relative;
- @apply rounded-md shadow-md ring-1 ring-inset ring-zinc-700;
transition-duration: 200ms;
transition-property: max-height;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
+ @apply "rounded-md shadow-md ring-1 ring-inset ring-zinc-700";
}
.main-toolbar-container[data-mode="default"]>div {
- @apply max-h-9;
+ @apply "max-h-9";
}
.main-toolbar-container[data-mode="drawer"]>div {
- @apply max-h-48;
+ @apply "max-h-48";
}
.absolute-toolbar-space {
position: absolute;
@@ -60,7 +60,7 @@
flex-direction: column;
justify-content: end;
align-items: center;
- @apply h-48;
+ @apply "h-48";
}
.absolute-toolbar-space .toolbar-buttons {
display: flex;
@@ -71,13 +71,13 @@
transition-duration: 200ms;
transition-property: padding-bottom;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
- @apply h-9 w-fit px-1;
+ @apply "h-9 w-fit px-1";
}
.main-toolbar-container[data-mode="default"] .absolute-toolbar-space .toolbar-buttons {
- @apply pb-0;
+ @apply "pb-0";
}
.main-toolbar-container[data-mode="drawer"] .absolute-toolbar-space .toolbar-buttons {
- @apply pb-1.5;
+ @apply "pb-1.5";
}
.toolbar-item {
display: flex;
@@ -85,20 +85,20 @@
justify-content: center;
background-color: transparent;
position: relative;
- @apply h-9 w-8 ;
+ @apply "h-9 w-8";
}
.toolbar-item>img , .toolbar-item img {
-webkit-user-drag: none !important;
- @apply h-5 w-5;
+ @apply "h-5 w-5";
}
.toolbar-item-separator {
display: flex;
align-items: center;
justify-content: center;
- @apply h-9 w-1.5;
+ @apply "h-9 w-1.5";
}
.toolbar-item-separator>div {
- @apply h-5 w-px bg-white/50
+ @apply "h-5 w-px bg-white/50";
}
/*Drawer*/
@@ -110,7 +110,7 @@
justify-content: center;
padding: 0.75rem;
width: 100%;
- @apply mb-1 grow;
+ @apply "mb-1 grow";
}
.drawer-bottom-separator {
position: absolute;
@@ -129,38 +129,38 @@
transition-duration: 200ms;
transition-property: width;
transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
- @apply bg-zinc-500 rounded-full ;
+ @apply "bg-zinc-500 rounded-full";
}
.drawer-open-button-state {
- @apply h-8 w-8 bg-blue-500/50 rounded-md flex items-center justify-center;
+ @apply "h-8 w-8 bg-blue-500/50 rounded-md flex items-center justify-center";
}
.main-toolbar-container[data-mode="default"] .drawer-bottom-separator>div {
- @apply w-0;
+ @apply "w-0";
}
.main-toolbar-container[data-mode="drawer"] .drawer-bottom-separator>div {
- @apply w-100%;
+ @apply "w-100%";
}
.drawer-items {
display: grid;
grid-template-columns:repeat(5, minmax(0, 1fr));
width: 100%;
height: 100%;
- @apply p-2 gap-2;
+ @apply "p-2 gap-2";
}
.drawer-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
- @apply h-full w-full;
+ @apply "h-full w-full";
}
.drawer-item img {
-webkit-user-drag: none !important;
}
.drawer-item .drawer-item-text {
font-size: 0.72rem;
- @apply text-white mt-1;
+ @apply "text-white mt-1";
}
/*Toolbar Left Right Containers*/
@@ -175,10 +175,10 @@
width: 100vw;
z-index: 50;
pointer-events: none;
- @apply bottom-1;
+ @apply "bottom-1";
}
.toolbar-right-container:nth-child(0) , .toolbar-left-container:last-child {
- @apply shrink-0 h-9 bg-transparent;
+ @apply "shrink-0 h-9 bg-transparent";
}
.end-slideshow-button *,.mainmenu-button * {
-webkit-user-drag: none !important;
@@ -190,7 +190,7 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
- @apply h-9 w-9 rounded-md bg-red-700/75 ring-1 ring-inset ring-red-500 shadow-md;
+ @apply "h-9 w-9 rounded-md bg-red-700/75 ring-1 ring-inset ring-red-500 shadow-md";
}
.mainmenu-button {
pointer-events: auto;
@@ -199,7 +199,7 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
- @apply h-9 w-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700;
+ @apply "h-9 w-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700";
}
/*BottomSidebar*/
@@ -212,7 +212,7 @@
display: flex;
align-items: center;
z-index: 50;
- @apply bottom-1
+ @apply "bottom-1";
}
.bottom-sidebar-container._left {
justify-content: start;
@@ -227,10 +227,10 @@
overflow: hidden;
pointer-events: auto;
position: relative;
- @apply h-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700;
transition-duration: 200ms;
transition-property: max-height;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
+ @apply "h-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700";
}
.absolute-bottom-sidebar-space {
position: absolute;
@@ -241,7 +241,7 @@
flex-direction: column;
justify-content: end;
align-items: center;
- @apply h-9;
+ @apply "h-9";
}
.absolute-bottom-sidebar-space .toolbar-buttons {
display: flex;
@@ -252,7 +252,7 @@
transition-duration: 200ms;
transition-property: padding-bottom;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
- @apply h-9 w-fit px-1;
+ @apply "h-9 w-fit px-1";
}
/*Pagination Indicator*/
@@ -261,13 +261,13 @@
align-items: center;
justify-content: center;
width: 4.5rem;
- @apply h-9;
+ @apply "h-9";
}
.pagination-indicator .now-page-text {
- @apply text-white text-3.5 font-bold leading-none mr-0.5;
+ @apply "text-white text-3.5 font-bold leading-none mr-0.5";
}
.pagination-indicator .total-page-text {
- @apply text-white text-3 leading-none;
+ @apply "text-white text-3 leading-none";
}
/*Toolbar Item Animation*/
@@ -280,19 +280,19 @@
transition-property: all;
z-index: 0;
transition-timing-function: cubic-bezier(0.85, 0, 0.15, 1); !important;
- @apply duration-100 scale-75 opacity-0;
+ @apply "duration-100 scale-75 opacity-0";
}
.toolbar-item>img.icon-solid , .toolbar-item img.icon-solid {
display: none;
}
.toolbar-item .tb-animate-bg>div {
- @apply h-7.5 w-7.5 bg-zinc-300/12 rounded-0.275rem;
+ @apply "h-7.5 w-7.5 bg-zinc-300/12 rounded-0.275rem";
}
.toolbar-item.toolbar-item-active .tb-animate-bg>div {
- @apply bg-blue-300/15 ring-1 ring-inset ring-blue-400/50
+ @apply "bg-blue-300/15 ring-1 ring-inset ring-blue-400/50";
}
.toolbar-item.gest-pointer-down .tb-animate-bg , .toolbar-item.toolbar-item-active .tb-animate-bg{
- @apply scale-100 opacity-100;
+ @apply "scale-100 opacity-100";
}
.toolbar-item.gest-pointer-down>img.icon-solid , .toolbar-item.gest-pointer-down img.icon-solid ,
.toolbar-item.toolbar-item-active>img.icon-solid , .toolbar-item.toolbar-item-active img.icon-solid{
@@ -314,7 +314,7 @@
display: flex;
align-items: center;
z-index: 50;
- @apply bottom-35vh
+ @apply "bottom-35vh";
}
.screen-sidebar-container._left {
justify-content: start;
@@ -329,7 +329,7 @@
overflow: hidden;
pointer-events: auto;
position: relative;
- @apply w-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700;
+ @apply "w-9 rounded-md shadow-md ring-1 ring-inset ring-zinc-700";
}
.absolute-screen-sidebar-space {
position: absolute;
@@ -340,7 +340,7 @@
flex-direction: column;
justify-content: center;
align-items: center;
- @apply w-9;
+ @apply "w-9";
}
.absolute-screen-sidebar-space .toolbar-buttons {
display: flex;
@@ -351,7 +351,7 @@
transition-duration: 200ms;
transition-property: padding-bottom;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
- @apply w-9 h-fit py-1;
+ @apply "w-9 h-fit py-1";
}
.absolute-screen-sidebar-space .toolbar-item {
@@ -360,16 +360,16 @@
justify-content: center;
background-color: transparent;
position: relative;
- @apply h-8 w-9 ;
+ @apply "h-8 w-9";
}
.absolute-screen-sidebar-space .toolbar-item-separator {
display: flex;
align-items: center;
justify-content: center;
- @apply h-1.5 w-9;
+ @apply "h-1.5 w-9";
}
.absolute-screen-sidebar-space .toolbar-item-separator>div {
- @apply h-px w-5 bg-white/50
+ @apply "h-px w-5 bg-white/50";
}
.absolute-screen-sidebar-space .pagination-indicator {
@@ -377,20 +377,34 @@
flex-direction: column;
align-items: center;
justify-content: center;
- @apply w-9 h-10;
+ @apply "w-9 h-10";
}
.absolute-screen-sidebar-space .pagination-indicator .now-page-text {
- @apply text-white text-3.35 font-normal leading-none;
+ @apply "text-white text-3.35 font-normal leading-none";
}
.absolute-screen-sidebar-space .pagination-indicator .total-page-text {
- @apply text-white/75 mt-1 text-2.25 leading-none;
+ @apply "text-white/75 mt-1 text-2.25 leading-none";
}
-
/*Watermark*/
.brand-watermark {
position: absolute;
top: 0.25rem;
left: 0.25rem;
- @apply h-6;
+ @apply "h-6";
+}
+
+/*Pen Overlays*/
+.pen-overlays {
+ z-index: 60;
+ position: fixed;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ overflow: clip;
+ @apply "w-56 h-48 mb-2.5 bg-zinc-900/80 rounded-md ring-1 ring-zinc-700";
+}
+.pen-overlays * {
+ user-select: none !important;
+ -webkit-user-drag: none !important;
}
\ No newline at end of file