[feature]初步實現了Overlay Panel機制;禁用了HMR並修改了@apply的寫法以確保UnoCSS正常轉換styles

This commit is contained in:
Dubi906w 2024-05-01 20:16:18 +08:00
parent 0bf3133a3d
commit 47dc80afe6
7 changed files with 217 additions and 97 deletions

View File

@ -11,6 +11,9 @@ export default defineConfig({
plugins: [externalizeDepsPlugin()]
},
renderer: {
server: {
hmr: false,
},
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')

View File

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

View File

@ -1,35 +1,94 @@
// 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);
// classNameanimation
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 (
<>
<div onClick={() => {
if (v.action === "drawer") {
setToolbarSignal({
...toolbarSignal(),
isDrawerOpen: !toolbarSignal().isDrawerOpen
});
} else if (v.action&&v.action===toolbarSignal().activeTool&&v.overlay) {
// BoundingRectOverlay
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",
@ -60,5 +119,12 @@ export default function ToolBarButton(props) {
</div>
</> : void 0}
</div>
<Show when={v.type==="button"&&isOverlayDisplay()===true}>
<Dynamic component={v.overlay??null}
boundingRect={boundingRect()}
animateClass={className}
/>
</Show>
</>
)
}

View File

View File

@ -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 (
<>
<Portal>
<div class={`pen-overlays ${animateClass()}`} style={{
left: `${(boundingRect.left-(boundingRect.width/4)).toFixed(3)}px`,
bottom: `${(boundingRect.bottom-boundingRect.y).toFixed(3)}px`,
}}>
<div class="h-9 w-full bg-zinc-800 border-b border-zinc-600"></div>
</div>
</Portal>
</>
)
}

View File

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

View File

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