feat: 创建设备布点模块
@ -0,0 +1,67 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"computed": true,
|
||||
"createApp": true,
|
||||
"customRef": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"effectScope": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"inject": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"provide": true,
|
||||
"reactive": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"resolveComponent": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"toRaw": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"unref": true,
|
||||
"useAttrs": true,
|
||||
"useCssModule": true,
|
||||
"useCssVars": true,
|
||||
"useSlots": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import("vue")["EffectScope"];
|
||||
const computed: typeof import("vue")["computed"];
|
||||
const createApp: typeof import("vue")["createApp"];
|
||||
const customRef: typeof import("vue")["customRef"];
|
||||
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
|
||||
const defineComponent: typeof import("vue")["defineComponent"];
|
||||
const effectScope: typeof import("vue")["effectScope"];
|
||||
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
|
||||
const getCurrentScope: typeof import("vue")["getCurrentScope"];
|
||||
const h: typeof import("vue")["h"];
|
||||
const inject: typeof import("vue")["inject"];
|
||||
const isProxy: typeof import("vue")["isProxy"];
|
||||
const isReactive: typeof import("vue")["isReactive"];
|
||||
const isReadonly: typeof import("vue")["isReadonly"];
|
||||
const isRef: typeof import("vue")["isRef"];
|
||||
const markRaw: typeof import("vue")["markRaw"];
|
||||
const nextTick: typeof import("vue")["nextTick"];
|
||||
const onActivated: typeof import("vue")["onActivated"];
|
||||
const onBeforeMount: typeof import("vue")["onBeforeMount"];
|
||||
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
|
||||
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
|
||||
const onDeactivated: typeof import("vue")["onDeactivated"];
|
||||
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
|
||||
const onMounted: typeof import("vue")["onMounted"];
|
||||
const onRenderTracked: typeof import("vue")["onRenderTracked"];
|
||||
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
|
||||
const onScopeDispose: typeof import("vue")["onScopeDispose"];
|
||||
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
|
||||
const onUnmounted: typeof import("vue")["onUnmounted"];
|
||||
const onUpdated: typeof import("vue")["onUpdated"];
|
||||
const provide: typeof import("vue")["provide"];
|
||||
const reactive: typeof import("vue")["reactive"];
|
||||
const readonly: typeof import("vue")["readonly"];
|
||||
const ref: typeof import("vue")["ref"];
|
||||
const resolveComponent: typeof import("vue")["resolveComponent"];
|
||||
const shallowReactive: typeof import("vue")["shallowReactive"];
|
||||
const shallowReadonly: typeof import("vue")["shallowReadonly"];
|
||||
const shallowRef: typeof import("vue")["shallowRef"];
|
||||
const toRaw: typeof import("vue")["toRaw"];
|
||||
const toRef: typeof import("vue")["toRef"];
|
||||
const toRefs: typeof import("vue")["toRefs"];
|
||||
const toValue: typeof import("vue")["toValue"];
|
||||
const triggerRef: typeof import("vue")["triggerRef"];
|
||||
const unref: typeof import("vue")["unref"];
|
||||
const useAttrs: typeof import("vue")["useAttrs"];
|
||||
const useCssModule: typeof import("vue")["useCssModule"];
|
||||
const useCssVars: typeof import("vue")["useCssVars"];
|
||||
const useSlots: typeof import("vue")["useSlots"];
|
||||
const watch: typeof import("vue")["watch"];
|
||||
const watchEffect: typeof import("vue")["watchEffect"];
|
||||
const watchPostEffect: typeof import("vue")["watchPostEffect"];
|
||||
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type {
|
||||
Component,
|
||||
ComponentPublicInstance,
|
||||
ComputedRef,
|
||||
ExtractDefaultPropTypes,
|
||||
ExtractPropTypes,
|
||||
ExtractPublicPropTypes,
|
||||
InjectionKey,
|
||||
PropType,
|
||||
Ref,
|
||||
VNode,
|
||||
WritableComputedRef
|
||||
} from "vue";
|
||||
import("vue");
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<circle cx="9.99609" cy="9" r="5" fill="white"/>
|
||||
<circle cx="9.99609" cy="9" r="4.75" stroke="black" stroke-opacity="0.3" stroke-width="0.5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0.996094" y="0" width="18" height="18" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.137674 0 0 0 0 0.190937 0 0 0 0 0.270833 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 904 B |
@ -0,0 +1,17 @@
|
||||
<svg width="12" height="24" viewBox="0 0 12 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<rect x="4" y="4" width="4" height="16" rx="2" fill="white"/>
|
||||
<rect x="4.25" y="4.25" width="3.5" height="15.5" rx="1.75" stroke="black" stroke-opacity="0.3" stroke-width="0.5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0" y="0" width="12" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.137674 0 0 0 0 0.190937 0 0 0 0 0.270833 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 933 B |
@ -0,0 +1,17 @@
|
||||
<svg width="24" height="12" viewBox="0 0 24 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<rect x="20" y="4" width="4" height="16" rx="2" transform="rotate(90 20 4)" fill="white"/>
|
||||
<rect x="19.75" y="4.25" width="3.5" height="15.5" rx="1.75" transform="rotate(90 19.75 4.25)" stroke="black" stroke-opacity="0.3" stroke-width="0.5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0" y="0" width="24" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.137674 0 0 0 0 0.190937 0 0 0 0 0.270833 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 997 B |
@ -0,0 +1,20 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<circle cx="9" cy="9" r="5" fill="white"/>
|
||||
<circle cx="9" cy="9" r="4.75" stroke="black" stroke-opacity="0.3" stroke-width="0.5"/>
|
||||
</g>
|
||||
<path d="M10.8047 11.1242L9.49934 11.1242L9.49934 9.81885" stroke="black" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.94856 6.72607L8.25391 6.72607L8.25391 8.03142" stroke="black" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.69517 6.92267C10.007 7.03301 10.2858 7.22054 10.5055 7.46776C10.7252 7.71497 10.8787 8.01382 10.9517 8.33642C11.0247 8.65902 11.0148 8.99485 10.9229 9.31258C10.831 9.63031 10.6601 9.91958 10.4262 10.1534L9.49701 11.0421M8.25792 6.72607L7.30937 7.73554C7.07543 7.96936 6.90454 8.25863 6.81264 8.57636C6.72073 8.89408 6.71081 9.22992 6.78381 9.55251C6.8568 9.87511 7.01032 10.174 7.23005 10.4212C7.44978 10.6684 7.72855 10.8559 8.04036 10.9663" stroke="black" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0" y="0" width="18" height="18" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.137674 0 0 0 0 0.190937 0 0 0 0 0.270833 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,9 @@
|
||||
@font-face {
|
||||
font-family: "汉体";
|
||||
src: url("./cn/汉体.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "华康金刚黑";
|
||||
src: url("./cn/华康金刚黑.ttf");
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2022-09-05 22:54:14
|
||||
* @LastEditors: 秦少卫
|
||||
* @LastEditTime: 2022-09-05 22:59:30
|
||||
* @Description: 字体文件列表
|
||||
*/
|
||||
|
||||
const cnList = [
|
||||
{
|
||||
name: "汉体",
|
||||
fontFamily: "汉体"
|
||||
},
|
||||
{
|
||||
name: "华康金刚黑",
|
||||
fontFamily: "华康金刚黑"
|
||||
}
|
||||
];
|
||||
|
||||
const enList = [];
|
||||
|
||||
export default [...cnList, ...enList];
|
@ -0,0 +1,6 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 17.3334V28" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 22H8.66667L11.3333 18" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.66699 13.3334L22.316 25.0994C22.5303 25.2422 22.8079 25.2488 23.0287 25.1163L29.3337 21.3334" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.71876 8.47912C4.35533 8.23685 4.31919 7.71632 4.64565 7.42612L9.61867 3.00568C9.84374 2.8056 10.175 2.78127 10.4269 2.94631L28.4698 14.7675C28.8747 15.0329 28.8708 15.6277 28.4624 15.8876L22.3665 19.7669C22.1437 19.9086 21.8584 19.9055 21.6387 19.7591L4.71876 8.47912Z" stroke="#333333" stroke-width="1.5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 843 B |
@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="39" viewBox="0 0 36 39" fill="none">
|
||||
<g filter="url(#filter0_d_4310_40111)">
|
||||
<mask id="path-1-inside-1_4310_40111" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z" fill="white"/>
|
||||
<path d="M19.2704 25.9335L19.1658 24.939L18.6591 24.9924L18.4044 25.4335L19.2704 25.9335ZM15.6332 25.7667L16.4993 25.2667L16.273 24.8748L15.8294 24.7861L15.6332 25.7667ZM17.5 29L16.634 29.5L17.5 31L18.366 29.5L17.5 29ZM29 14C29 19.6811 24.6924 24.3575 19.1658 24.939L19.3751 26.9281C25.9088 26.2405 31 20.7155 31 14H29ZM18 3C24.0751 3 29 7.92487 29 14H31C31 6.8203 25.1797 1 18 1V3ZM7 14C7 7.92487 11.9249 3 18 3V1C10.8203 1 5 6.8203 5 14H7ZM15.8294 24.7861C10.7937 23.7788 7 19.3313 7 14H5C5 20.3032 9.48488 25.5566 15.4371 26.7472L15.8294 24.7861ZM14.7672 26.2667L16.634 29.5L18.366 28.5L16.4993 25.2667L14.7672 26.2667ZM18.366 29.5L20.1364 26.4335L18.4044 25.4335L16.634 28.5L18.366 29.5Z" fill="#DCDCDC" mask="url(#path-1-inside-1_4310_40111)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_4310_40111" x="0" y="0" width="36" height="39" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4310_40111"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4310_40111" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M14.3334 2H7.66675H1.66675" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.66675 7.66667V2" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.80841 6.52734L13.4336 9.37438L12.8528 10.254L11.5187 12.6573L10.9379 13.5369L1.60059 11.035L2.80841 6.52734Z" fill="#52C41A" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.8527 10.2539L14.1406 10.599L13.4504 13.1748L11.5186 12.6572" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="4.5" cy="9.5" r="1.25" stroke="white" stroke-width="0.5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 814 B |
@ -0,0 +1,27 @@
|
||||
<svg width="131" height="66" viewBox="0 0 131 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4785_119799)">
|
||||
<mask id="path-1-inside-1_4785_119799" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z" fill="white"/>
|
||||
<path d="M58.8 48L59.5433 47.331L59.2454 47H58.8V48ZM66 56L65.2567 56.669L66 57.4948L66.7433 56.669L66 56ZM73.2 48V47H72.7546L72.4567 47.331L73.2 48ZM7 6C7 4.34315 8.34315 3 10 3V1C7.23858 1 5 3.23857 5 6H7ZM7 44V6H5V44H7ZM10 47C8.34315 47 7 45.6569 7 44H5C5 46.7614 7.23858 49 10 49V47ZM58.8 47H10V49H58.8V47ZM58.0567 48.669L65.2567 56.669L66.7433 55.331L59.5433 47.331L58.0567 48.669ZM66.7433 56.669L73.9433 48.669L72.4567 47.331L65.2567 55.331L66.7433 56.669ZM121 47H73.2V49H121V47ZM124 44C124 45.6569 122.657 47 121 47V49C123.761 49 126 46.7614 126 44H124ZM124 6V44H126V6H124ZM121 3C122.657 3 124 4.34315 124 6H126C126 3.23858 123.761 1 121 1V3ZM10 3H121V1H10V3Z" fill="#154DDD" fill-opacity="0.2" mask="url(#path-1-inside-1_4785_119799)"/>
|
||||
</g>
|
||||
<rect x="14" y="9" width="32" height="32" rx="2" fill="#FAAD14"/>
|
||||
<path d="M40.0018 16H29.5301H20.1055" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.5273 24.8419V16" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.8972 23.0625L38.5869 27.5048L37.6746 28.8773L35.579 32.6272L34.6667 33.9997L20 30.0959L21.8972 23.0625Z" fill="white" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M37.6737 28.8789L39.6967 29.4174L38.6126 33.4365L35.5781 32.6288" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26.6615 27.7018C26.6615 28.8548 25.7201 29.7923 24.5554 29.7923C23.3906 29.7923 22.4492 28.8548 22.4492 27.7018C22.4492 26.5488 23.3906 25.6113 24.5554 25.6113C25.7201 25.6113 26.6615 26.5488 26.6615 27.7018Z" stroke="#FAAD14" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4785_119799" x="0" y="0" width="131" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4785_119799"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4785_119799" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,26 @@
|
||||
<svg width="36" height="39" viewBox="0 0 36 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4310_40100)">
|
||||
<mask id="path-1-inside-1_4310_40100" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z" fill="#154DDD" fill-opacity="0.05" shape-rendering="crispEdges"/>
|
||||
<path d="M19.2704 25.9335L19.1658 24.939L18.6591 24.9924L18.4044 25.4335L19.2704 25.9335ZM15.6332 25.7667L16.4993 25.2667L16.273 24.8748L15.8294 24.7861L15.6332 25.7667ZM17.5 29L16.634 29.5L17.5 31L18.366 29.5L17.5 29ZM29 14C29 19.6811 24.6924 24.3575 19.1658 24.939L19.3751 26.9281C25.9088 26.2405 31 20.7155 31 14H29ZM18 3C24.0751 3 29 7.92487 29 14H31C31 6.8203 25.1797 1 18 1V3ZM7 14C7 7.92487 11.9249 3 18 3V1C10.8203 1 5 6.8203 5 14H7ZM15.8294 24.7861C10.7937 23.7788 7 19.3313 7 14H5C5 20.3032 9.48488 25.5566 15.4371 26.7472L15.8294 24.7861ZM14.7672 26.2667L16.634 29.5L18.366 28.5L16.4993 25.2667L14.7672 26.2667ZM18.366 29.5L20.1364 26.4335L18.4044 25.4335L16.634 28.5L18.366 29.5Z" fill="#154DDD" mask="url(#path-1-inside-1_4310_40100)"/>
|
||||
</g>
|
||||
<path d="M24.3334 8H17.6667H11.6667" stroke="#E80D0D" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.6667 13.6667V8" stroke="#E80D0D" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.8084 12.5273L23.4336 15.3744L22.8528 16.254L21.5186 18.6573L20.9379 19.5369L11.6006 17.035L12.8084 12.5273Z" fill="#E80D0D" stroke="#E80D0D" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22.8527 16.2539L24.1406 16.599L23.4504 19.1748L21.5186 18.6572" stroke="#E80D0D" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="14.5" cy="15.5" r="1.25" stroke="white" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4310_40100" x="0" y="0" width="36" height="39" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4310_40100"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4310_40100" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,33 @@
|
||||
<svg width="153" height="145" viewBox="0 0 153 145" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="60" y="129" width="30" height="2" fill="#F39800"/>
|
||||
<rect x="76" y="132.5" width="12" height="2" transform="rotate(90 76 132.5)" fill="#F39800"/>
|
||||
<line x1="75.25" y1="126" x2="75.25" y2="48" stroke="#00F0FF" stroke-width="1.5"/>
|
||||
<circle cx="75" cy="130" r="5" fill="#18EFBA"/>
|
||||
<path d="M69.3427 123.47C67.3037 125.072 66 127.52 66 130.264C66 135.089 70.0294 139 75 139C79.9706 139 84 135.089 84 130.264C84 127.236 82.412 124.567 80 123" stroke="white" stroke-width="2" stroke-linecap="square"/>
|
||||
<g filter="url(#filter0_d_9883_75580)">
|
||||
<mask id="path-6-inside-1_9883_75580" fill="white">
|
||||
<path d="M6 6C6 3.79086 7.79086 2 10 2H143C145.209 2 147 3.79086 147 6V44C147 46.2091 145.209 48 143 48H10C7.79086 48 6 46.2091 6 44V6Z"/>
|
||||
</mask>
|
||||
<path d="M6 6C6 3.79086 7.79086 2 10 2H143C145.209 2 147 3.79086 147 6V44C147 46.2091 145.209 48 143 48H10C7.79086 48 6 46.2091 6 44V6Z" fill="#00183E" fill-opacity="0.9" shape-rendering="crispEdges"/>
|
||||
<path d="M10 3.5H143V0.5H10V3.5ZM145.5 6V44H148.5V6H145.5ZM143 46.5H10V49.5H143V46.5ZM7.5 44V6H4.5V44H7.5ZM10 46.5C8.61929 46.5 7.5 45.3807 7.5 44H4.5C4.5 47.0376 6.96243 49.5 10 49.5V46.5ZM145.5 44C145.5 45.3807 144.381 46.5 143 46.5V49.5C146.038 49.5 148.5 47.0376 148.5 44H145.5ZM143 3.5C144.381 3.5 145.5 4.61929 145.5 6H148.5C148.5 2.96244 146.038 0.5 143 0.5V3.5ZM10 0.5C6.96243 0.5 4.5 2.96243 4.5 6H7.5C7.5 4.61929 8.61929 3.5 10 3.5V0.5Z" fill="#00F0FF" mask="url(#path-6-inside-1_9883_75580)"/>
|
||||
</g>
|
||||
<rect x="14" y="9" width="32" height="32" rx="2" fill="#52C41A"/>
|
||||
<path d="M40.0018 16H29.5301H20.1055" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.5273 24.8419V16" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.8972 23.0625L38.5869 27.5048L37.6746 28.8773L35.579 32.6272L34.6667 33.9997L20 30.0959L21.8972 23.0625Z" fill="white" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M37.6737 28.8782L39.6967 29.4166L38.6126 33.4357L35.5781 32.6281" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26.6615 27.7026C26.6615 28.8555 25.7201 29.793 24.5554 29.793C23.3906 29.793 22.4492 28.8555 22.4492 27.7026C22.4492 26.5496 23.3906 25.6121 24.5554 25.6121C25.7201 25.6121 26.6615 26.5496 26.6615 27.7026Z" stroke="#52C41A" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_9883_75580" x="0" y="0" width="153" height="58" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_9883_75580"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_9883_75580" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@ -0,0 +1,26 @@
|
||||
<svg width="32" height="35" viewBox="0 0 32 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4341_40811)">
|
||||
<mask id="path-1-inside-1_4341_40811" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.2704 24.9335C23.3006 24.299 28 19.1983 28 13C28 6.37258 22.6274 1 16 1C9.37258 1 4 6.37258 4 13C4 18.8172 8.1393 23.6677 13.6332 24.7667L15.5 28L17.2704 24.9335Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.2704 24.9335C23.3006 24.299 28 19.1983 28 13C28 6.37258 22.6274 1 16 1C9.37258 1 4 6.37258 4 13C4 18.8172 8.1393 23.6677 13.6332 24.7667L15.5 28L17.2704 24.9335Z" fill="#154DDD" fill-opacity="0.05" shape-rendering="crispEdges"/>
|
||||
<path d="M17.2704 24.9335L17.1658 23.939L16.6591 23.9924L16.4044 24.4335L17.2704 24.9335ZM13.6332 24.7667L14.4993 24.2667L14.273 23.8748L13.8294 23.7861L13.6332 24.7667ZM15.5 28L14.634 28.5L15.5 30L16.366 28.5L15.5 28ZM27 13C27 18.6811 22.6924 23.3575 17.1658 23.939L17.3751 25.9281C23.9088 25.2405 29 19.7155 29 13H27ZM16 2C22.0751 2 27 6.92487 27 13H29C29 5.8203 23.1797 0 16 0V2ZM5 13C5 6.92487 9.92487 2 16 2V0C8.8203 0 3 5.8203 3 13H5ZM13.8294 23.7861C8.79373 22.7788 5 18.3313 5 13H3C3 19.3032 7.48488 24.5566 13.4371 25.7472L13.8294 23.7861ZM12.7672 25.2667L14.634 28.5L16.366 27.5L14.4993 24.2667L12.7672 25.2667ZM16.366 28.5L18.1364 25.4335L16.4044 24.4335L14.634 27.5L16.366 28.5Z" fill="#154DDD" mask="url(#path-1-inside-1_4341_40811)"/>
|
||||
</g>
|
||||
<path d="M22.3333 7H15.6667H9.66666" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.6667 12.6667V7" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.8083 11.5273L21.4335 14.3744L20.8527 15.254L19.5186 17.6573L18.9378 18.5369L9.60049 16.035L10.8083 11.5273Z" fill="#52C41A" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20.8528 15.2539L22.1407 15.599L21.4505 18.1748L19.5186 17.6572" stroke="#52C41A" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="12.5" cy="14.5" r="1.25" stroke="white" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4341_40811" x="0" y="0" width="32" height="35" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="3"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4341_40811"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4341_40811" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,29 @@
|
||||
<svg width="153" height="145" viewBox="0 0 153 145" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="60" y="129" width="30" height="2" fill="#F39800"/>
|
||||
<rect x="76" y="132.5" width="12" height="2" transform="rotate(90 76 132.5)" fill="#F39800"/>
|
||||
<line x1="75.25" y1="126" x2="75.25" y2="48" stroke="#00F0FF" stroke-width="1.5"/>
|
||||
<circle cx="75" cy="130" r="5" fill="#18EFBA"/>
|
||||
<path d="M69.3427 123.47C67.3037 125.072 66 127.52 66 130.264C66 135.089 70.0294 139 75 139C79.9706 139 84 135.089 84 130.264C84 127.236 82.412 124.567 80 123" stroke="white" stroke-width="2" stroke-linecap="square"/>
|
||||
<g filter="url(#filter0_d_10037_91857)">
|
||||
<path d="M6 6C6 3.79086 7.79086 2 10 2H143C145.209 2 147 3.79086 147 6V44C147 46.2091 145.209 48 143 48H10C7.79086 48 6 46.2091 6 44V6Z" fill="#00183E" fill-opacity="0.9" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
<rect x="14" y="9" width="32" height="32" rx="2" fill="#52C41A"/>
|
||||
<path d="M40.0018 16H29.5301H20.1055" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.5273 24.8419V16" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.8972 23.0625L38.5869 27.5048L37.6746 28.8773L35.579 32.6272L34.6667 33.9997L20 30.0959L21.8972 23.0625Z" fill="white" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M37.6737 28.8782L39.6967 29.4166L38.6126 33.4357L35.5781 32.6281" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26.6615 27.7026C26.6615 28.8555 25.7201 29.793 24.5554 29.793C23.3906 29.793 22.4492 28.8555 22.4492 27.7026C22.4492 26.5496 23.3906 25.6121 24.5554 25.6121C25.7201 25.6121 26.6615 26.5496 26.6615 27.7026Z" stroke="#52C41A" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_10037_91857" x="0" y="0" width="153" height="58" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_10037_91857"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_10037_91857" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,26 @@
|
||||
<svg width="36" height="39" viewBox="0 0 36 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4310_40080)">
|
||||
<mask id="path-1-inside-1_4310_40080" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z" fill="#154DDD" fill-opacity="0.05" shape-rendering="crispEdges"/>
|
||||
<path d="M19.2704 25.9335L19.1658 24.939L18.6591 24.9924L18.4044 25.4335L19.2704 25.9335ZM15.6332 25.7667L16.4993 25.2667L16.273 24.8748L15.8294 24.7861L15.6332 25.7667ZM17.5 29L16.634 29.5L17.5 31L18.366 29.5L17.5 29ZM29 14C29 19.6811 24.6924 24.3575 19.1658 24.939L19.3751 26.9281C25.9088 26.2405 31 20.7155 31 14H29ZM18 3C24.0751 3 29 7.92487 29 14H31C31 6.8203 25.1797 1 18 1V3ZM7 14C7 7.92487 11.9249 3 18 3V1C10.8203 1 5 6.8203 5 14H7ZM15.8294 24.7861C10.7937 23.7788 7 19.3313 7 14H5C5 20.3032 9.48488 25.5566 15.4371 26.7472L15.8294 24.7861ZM14.7672 26.2667L16.634 29.5L18.366 28.5L16.4993 25.2667L14.7672 26.2667ZM18.366 29.5L20.1364 26.4335L18.4044 25.4335L16.634 28.5L18.366 29.5Z" fill="#154DDD" mask="url(#path-1-inside-1_4310_40080)"/>
|
||||
</g>
|
||||
<path d="M24.3333 8H17.6666H11.6666" stroke="#CCCCCC" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.6666 13.6667V8" stroke="#CCCCCC" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.8083 12.5273L23.4335 15.3744L22.8527 16.254L21.5185 18.6573L20.9377 19.5369L11.6005 17.035L12.8083 12.5273Z" fill="#CCCCCC" stroke="#CCCCCC" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22.8528 16.2539L24.1407 16.599L23.4505 19.1748L21.5187 18.6572" stroke="#CCCCCC" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="14.5" cy="15.5" r="1.25" stroke="white" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4310_40080" x="0" y="0" width="36" height="39" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4310_40080"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4310_40080" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,27 @@
|
||||
<svg width="131" height="66" viewBox="0 0 131 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4785_117861)">
|
||||
<mask id="path-1-inside-1_4785_117861" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z" fill="white"/>
|
||||
<path d="M58.8 48L59.5433 47.331L59.2454 47H58.8V48ZM66 56L65.2567 56.669L66 57.4948L66.7433 56.669L66 56ZM73.2 48V47H72.7546L72.4567 47.331L73.2 48ZM7 6C7 4.34315 8.34315 3 10 3V1C7.23858 1 5 3.23857 5 6H7ZM7 44V6H5V44H7ZM10 47C8.34315 47 7 45.6569 7 44H5C5 46.7614 7.23858 49 10 49V47ZM58.8 47H10V49H58.8V47ZM58.0567 48.669L65.2567 56.669L66.7433 55.331L59.5433 47.331L58.0567 48.669ZM66.7433 56.669L73.9433 48.669L72.4567 47.331L65.2567 55.331L66.7433 56.669ZM121 47H73.2V49H121V47ZM124 44C124 45.6569 122.657 47 121 47V49C123.761 49 126 46.7614 126 44H124ZM124 6V44H126V6H124ZM121 3C122.657 3 124 4.34315 124 6H126C126 3.23858 123.761 1 121 1V3ZM10 3H121V1H10V3Z" fill="#154DDD" fill-opacity="0.2" mask="url(#path-1-inside-1_4785_117861)"/>
|
||||
</g>
|
||||
<rect x="14" y="9" width="32" height="32" rx="2" fill="#E80D0D"/>
|
||||
<path d="M40.0018 16H29.5301H20.1055" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.5273 24.8419V16" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.8972 23.0625L38.5869 27.5048L37.6746 28.8773L35.579 32.6272L34.6667 33.9997L20 30.0959L21.8972 23.0625Z" fill="white" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M37.6737 28.8789L39.6967 29.4174L38.6126 33.4365L35.5781 32.6288" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26.6615 27.7018C26.6615 28.8548 25.7201 29.7923 24.5554 29.7923C23.3906 29.7923 22.4492 28.8548 22.4492 27.7018C22.4492 26.5488 23.3906 25.6113 24.5554 25.6113C25.7201 25.6113 26.6615 26.5488 26.6615 27.7018Z" stroke="#E80D0D" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4785_117861" x="0" y="0" width="131" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4785_117861"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4785_117861" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,26 @@
|
||||
<svg width="36" height="39" viewBox="0 0 36 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_4517_70725)">
|
||||
<mask id="path-1-inside-1_4517_70725" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2704 25.9335C25.3006 25.299 30 20.1983 30 14C30 7.37258 24.6274 2 18 2C11.3726 2 6 7.37258 6 14C6 19.8172 10.1393 24.6677 15.6332 25.7667L17.5 29L19.2704 25.9335Z" fill="#154DDD" fill-opacity="0.05" shape-rendering="crispEdges"/>
|
||||
<path d="M19.2704 25.9335L19.1658 24.939L18.6591 24.9924L18.4044 25.4335L19.2704 25.9335ZM15.6332 25.7667L16.4993 25.2667L16.273 24.8748L15.8294 24.7861L15.6332 25.7667ZM17.5 29L16.634 29.5L17.5 31L18.366 29.5L17.5 29ZM29 14C29 19.6811 24.6924 24.3575 19.1658 24.939L19.3751 26.9281C25.9088 26.2405 31 20.7155 31 14H29ZM18 3C24.0751 3 29 7.92487 29 14H31C31 6.8203 25.1797 1 18 1V3ZM7 14C7 7.92487 11.9249 3 18 3V1C10.8203 1 5 6.8203 5 14H7ZM15.8294 24.7861C10.7937 23.7788 7 19.3313 7 14H5C5 20.3032 9.48488 25.5566 15.4371 26.7472L15.8294 24.7861ZM14.7672 26.2667L16.634 29.5L18.366 28.5L16.4993 25.2667L14.7672 26.2667ZM18.366 29.5L20.1364 26.4335L18.4044 25.4335L16.634 28.5L18.366 29.5Z" fill="#154DDD" mask="url(#path-1-inside-1_4517_70725)"/>
|
||||
</g>
|
||||
<path d="M24.3333 8H17.6666H11.6666" stroke="#FAAD14" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.6666 13.6667V8" stroke="#FAAD14" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.8083 12.5273L23.4335 15.3744L22.8527 16.254L21.5185 18.6573L20.9377 19.5369L11.6005 17.035L12.8083 12.5273Z" fill="#FAAD14" stroke="#FAAD14" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22.8528 16.2539L24.1407 16.599L23.4505 19.1748L21.5187 18.6572" stroke="#FAAD14" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="14.5" cy="15.5" r="1.25" stroke="white" stroke-width="0.5"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_4517_70725" x="0" y="0" width="36" height="39" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4517_70725"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4517_70725" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="131" height="66" viewBox="0 0 131 66" fill="none">
|
||||
<g filter="url(#filter0_d_4785_117771)">
|
||||
<mask id="path-1-inside-1_4785_117771" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V44C6 46.2091 7.79086 48 10 48H58.8L66 56L73.2 48H121C123.209 48 125 46.2091 125 44V6C125 3.79086 123.209 2 121 2H10Z" fill="white"/>
|
||||
<path d="M58.8 48L59.5433 47.331L59.2454 47H58.8V48ZM66 56L65.2567 56.669L66 57.4948L66.7433 56.669L66 56ZM73.2 48V47H72.7546L72.4567 47.331L73.2 48ZM7 6C7 4.34315 8.34315 3 10 3V1C7.23858 1 5 3.23857 5 6H7ZM7 44V6H5V44H7ZM10 47C8.34315 47 7 45.6569 7 44H5C5 46.7614 7.23858 49 10 49V47ZM58.8 47H10V49H58.8V47ZM58.0567 48.669L65.2567 56.669L66.7433 55.331L59.5433 47.331L58.0567 48.669ZM66.7433 56.669L73.9433 48.669L72.4567 47.331L65.2567 55.331L66.7433 56.669ZM121 47H73.2V49H121V47ZM124 44C124 45.6569 122.657 47 121 47V49C123.761 49 126 46.7614 126 44H124ZM124 6V44H126V6H124ZM121 3C122.657 3 124 4.34315 124 6H126C126 3.23858 123.761 1 121 1V3ZM10 3H121V1H10V3Z" fill="#154DDD" fill-opacity="0.2" mask="url(#path-1-inside-1_4785_117771)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_4785_117771" x="0" y="0" width="131" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4785_117771"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4785_117771" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,27 @@
|
||||
// 动效类型
|
||||
export const animationTypeConf: Record<string, any>[] = [
|
||||
{
|
||||
value: "None",
|
||||
label: "None"
|
||||
},
|
||||
{
|
||||
value: "Fade",
|
||||
label: "Fade"
|
||||
},
|
||||
{
|
||||
value: "Bounce",
|
||||
label: "Bounce"
|
||||
},
|
||||
{
|
||||
value: "Shake",
|
||||
label: "Shake"
|
||||
},
|
||||
{
|
||||
value: "Rotation",
|
||||
label: "Rotation"
|
||||
},
|
||||
{
|
||||
value: "Flash",
|
||||
label: "Flash"
|
||||
}
|
||||
];
|
@ -0,0 +1,103 @@
|
||||
// 通用元素
|
||||
export const baseTypeConf: string[] = [
|
||||
"text",
|
||||
"i-text",
|
||||
"textbox",
|
||||
"rect",
|
||||
"circle",
|
||||
"triangle",
|
||||
"polygon",
|
||||
"image",
|
||||
"group",
|
||||
"line",
|
||||
"arrow"
|
||||
];
|
||||
|
||||
// 文字元素
|
||||
export const textTypeConf: string[] = ["i-text", "textbox", "text"];
|
||||
|
||||
// 字体下拉列表
|
||||
export const strokeDashListConf: Record<string, any>[] = [
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [],
|
||||
strokeLineCap: "butt"
|
||||
},
|
||||
label: "Stroke"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [1, 10],
|
||||
strokeLineCap: "butt"
|
||||
},
|
||||
label: "Dash-1"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [1, 10],
|
||||
strokeLineCap: "round"
|
||||
},
|
||||
label: "Dash-2"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [15, 15],
|
||||
strokeLineCap: "square"
|
||||
},
|
||||
label: "Dash-3"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [15, 15],
|
||||
strokeLineCap: "round"
|
||||
},
|
||||
label: "Dash-4"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [25, 25],
|
||||
strokeLineCap: "square"
|
||||
},
|
||||
label: "Dash-5"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [25, 25],
|
||||
strokeLineCap: "round"
|
||||
},
|
||||
label: "Dash-6"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [1, 8, 16, 8, 1, 20],
|
||||
strokeLineCap: "square"
|
||||
},
|
||||
label: "Dash-7"
|
||||
},
|
||||
{
|
||||
value: {
|
||||
strokeUniform: true,
|
||||
strokeDashArray: [1, 8, 16, 8, 1, 20],
|
||||
strokeLineCap: "round"
|
||||
},
|
||||
label: "Dash-8"
|
||||
}
|
||||
];
|
||||
|
||||
// 对齐图标
|
||||
export const textAlignListSvgConf: string[] = [
|
||||
'<svg t="1650441458823" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3554" width="18" height="18"><path d="M198.4 198.4h341.333333c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533334 19.2v57.6c0 8.533333-2.133333 14.933333-8.533334 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-341.333333c-8.533333 0-14.933333-2.133333-19.2-8.533334-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533333z m0 170.666667h569.6c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333h-569.6c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533333z m0 170.666666h454.4c8.533333 0 14.933333 2.133333 19.2 8.533334 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333h-454.4c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533334z m0 170.666667h625.066667c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-625.066667c-8.533333 0-14.933333-2.133333-19.2-8.533334-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533333z" p-id="3555"></path></svg>',
|
||||
'<svg t="1650441512015" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3704" width="18" height="18"><path d="M313.6 198.4h398.933333c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533334 19.2v57.6c0 8.533333-2.133333 14.933333-8.533334 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-398.933333c-8.533333 0-14.933333-2.133333-19.2-8.533334-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 10.666667-8.533333 19.2-8.533333z m-115.2 170.666667h625.066667c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333h-625.066667c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533333z m115.2 170.666666h398.933333c8.533333 0 14.933333 2.133333 19.2 8.533334 6.4 6.4 8.533333 12.8 8.533334 19.2v57.6c0 8.533333-2.133333 14.933333-8.533334 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333h-398.933333c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 10.666667-8.533333 19.2-8.533334z m-115.2 170.666667h625.066667c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-625.066667c-8.533333 0-14.933333-2.133333-19.2-8.533334-6.4-6.4-8.533333-12.8-8.533333-19.2v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 4.266667-4.266667 12.8-8.533333 19.2-8.533333z" p-id="3705"></path></svg>',
|
||||
'<svg t="1650441519862" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3854" width="18" height="18"><path d="M454.4 283.733333v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 6.4-6.4 12.8-8.533333 19.2-8.533333h341.333334c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-341.333334c-8.533333 0-14.933333-2.133333-19.2-8.533334-4.266667-4.266667-8.533333-10.666667-8.533333-19.2z m-226.133333 170.666667v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 6.4-6.4 12.8-8.533333 19.2-8.533333h569.6c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333H256c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-4.266667-8.533333-10.666667-8.533333-19.2z m113.066666 170.666667v-57.6c0-8.533333 2.133333-14.933333 8.533334-19.2 6.4-6.4 12.8-8.533333 19.2-8.533334h454.4c8.533333 0 14.933333 2.133333 19.2 8.533334 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533333h-454.4c-8.533333 0-14.933333-2.133333-19.2-8.533333-6.4-4.266667-8.533333-10.666667-8.533334-19.2z m-170.666666 170.666666v-57.6c0-8.533333 2.133333-14.933333 8.533333-19.2 6.4-6.4 12.8-8.533333 19.2-8.533333h625.066667c8.533333 0 14.933333 2.133333 19.2 8.533333 6.4 6.4 8.533333 12.8 8.533333 19.2v57.6c0 8.533333-2.133333 14.933333-8.533333 19.2-6.4 6.4-12.8 8.533333-19.2 8.533334h-625.066667c-8.533333 0-14.933333-2.133333-19.2-8.533334-6.4-4.266667-8.533333-10.666667-8.533333-19.2z" p-id="3855"></path></svg>'
|
||||
];
|
||||
|
||||
// 字体对齐方式
|
||||
export const textAlignListConf: string[] = ["left", "center", "right"];
|
@ -0,0 +1 @@
|
||||
export const LANG = "lang"; // 多语言key
|
@ -0,0 +1,196 @@
|
||||
// UI类型
|
||||
export const uiType = {
|
||||
SELECT: "select",
|
||||
COLOR: "color",
|
||||
NUMBER: "number"
|
||||
};
|
||||
|
||||
// 有参数滤镜
|
||||
export const paramsFilters = [
|
||||
{
|
||||
type: "Brightness",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "brightness",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Contrast",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "contrast",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Saturation",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "saturation",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Vibrance",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "vibrance",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "HueRotation",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "rotation",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Noise",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "noise",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: -1,
|
||||
max: 1000,
|
||||
step: 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Pixelate",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "blocksize",
|
||||
value: 0.01,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0.01,
|
||||
max: 100,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Blur",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "blur",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "Grayscale",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "mode",
|
||||
value: "average",
|
||||
uiType: uiType.SELECT,
|
||||
list: ["average", "lightness", "luminosity"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "RemoveColor",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "color",
|
||||
value: "",
|
||||
uiType: uiType.COLOR
|
||||
},
|
||||
{
|
||||
key: "distance",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 组合式参数滤镜
|
||||
export const combinationFilters = [
|
||||
{
|
||||
type: "Gamma",
|
||||
status: false,
|
||||
params: [
|
||||
{
|
||||
key: "red",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0.01,
|
||||
max: 2.2,
|
||||
step: 0.01
|
||||
},
|
||||
{
|
||||
key: "green",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0.01,
|
||||
max: 2.2,
|
||||
step: 0.01
|
||||
},
|
||||
{
|
||||
key: "blue",
|
||||
value: 0,
|
||||
uiType: uiType.NUMBER,
|
||||
min: 0.01,
|
||||
max: 2.2,
|
||||
step: 0.01
|
||||
}
|
||||
],
|
||||
handler(
|
||||
red: number | string,
|
||||
green: number | string,
|
||||
blue: number | string
|
||||
) {
|
||||
return {
|
||||
gamma: [red, green, blue]
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
@ -0,0 +1,270 @@
|
||||
/* eslint-disable no-prototype-builtins */
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-05-25 22:33:23
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:31:16
|
||||
* @Description: 右键菜单
|
||||
*/
|
||||
class ContextMenu {
|
||||
constructor(container, items) {
|
||||
this.container = container;
|
||||
this.dom = null;
|
||||
this.shown = false;
|
||||
this.root = true;
|
||||
this.parent = null;
|
||||
this.submenus = [];
|
||||
this.items = items;
|
||||
|
||||
this._onclick = e => {
|
||||
if (
|
||||
this.dom &&
|
||||
e.target != this.dom &&
|
||||
e.target.parentElement != this.dom &&
|
||||
!e.target.classList.contains("item") &&
|
||||
!e.target.parentElement.classList.contains("item")
|
||||
) {
|
||||
this.hideAll();
|
||||
}
|
||||
};
|
||||
|
||||
this._oncontextmenu = e => {
|
||||
e.preventDefault();
|
||||
if (
|
||||
e.target != this.dom &&
|
||||
e.target.parentElement != this.dom &&
|
||||
!e.target.classList.contains("item") &&
|
||||
!e.target.parentElement.classList.contains("item")
|
||||
) {
|
||||
this.hideAll();
|
||||
this.show(e.clientX, e.clientY);
|
||||
}
|
||||
};
|
||||
|
||||
this._oncontextmenu_keydown = e => {
|
||||
if (e.keyCode != 93) return;
|
||||
e.preventDefault();
|
||||
|
||||
this.hideAll();
|
||||
this.show(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
this._onblur = () => {
|
||||
this.hideAll();
|
||||
};
|
||||
}
|
||||
|
||||
getMenuDom() {
|
||||
const menu = document.createElement("div");
|
||||
menu.classList.add("context");
|
||||
|
||||
for (const item of this.items) {
|
||||
menu.appendChild(this.itemToDomEl(item));
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
itemToDomEl(data) {
|
||||
const item = document.createElement("div");
|
||||
|
||||
if (data === null) {
|
||||
item.classList = "separator";
|
||||
return item;
|
||||
}
|
||||
|
||||
if (
|
||||
data.hasOwnProperty("color") &&
|
||||
/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(data.color.toString())
|
||||
) {
|
||||
item.style.cssText = `color: ${data.color}`;
|
||||
}
|
||||
|
||||
item.classList.add("item");
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.classList = "label";
|
||||
label.innerText = data.hasOwnProperty("text")
|
||||
? data["text"].toString()
|
||||
: "";
|
||||
item.appendChild(label);
|
||||
|
||||
if (data.hasOwnProperty("disabled") && data["disabled"]) {
|
||||
item.classList.add("disabled");
|
||||
} else {
|
||||
item.classList.add("enabled");
|
||||
}
|
||||
|
||||
const hotkey = document.createElement("span");
|
||||
hotkey.classList = "hotkey";
|
||||
hotkey.innerText = data.hasOwnProperty("hotkey")
|
||||
? data["hotkey"].toString()
|
||||
: "";
|
||||
item.appendChild(hotkey);
|
||||
|
||||
if (
|
||||
data.hasOwnProperty("subitems") &&
|
||||
Array.isArray(data["subitems"]) &&
|
||||
data["subitems"].length > 0
|
||||
) {
|
||||
const menu = new ContextMenu(this.container, data["subitems"]);
|
||||
menu.root = false;
|
||||
menu.parent = this;
|
||||
|
||||
const openSubItems = () => {
|
||||
if (data.hasOwnProperty("disabled") && data["disabled"] == true) return;
|
||||
|
||||
this.hideSubMenus();
|
||||
|
||||
const x = this.dom.offsetLeft + this.dom.clientWidth + item.offsetLeft;
|
||||
const y = this.dom.offsetTop + item.offsetTop;
|
||||
|
||||
if (!menu.shown) {
|
||||
menu.show(x, y);
|
||||
} else {
|
||||
menu.hide();
|
||||
}
|
||||
};
|
||||
|
||||
this.submenus.push(menu);
|
||||
|
||||
item.classList.add("has-subitems");
|
||||
item.addEventListener("click", openSubItems);
|
||||
item.addEventListener("mousemove", openSubItems);
|
||||
} else if (
|
||||
data.hasOwnProperty("submenu") &&
|
||||
data["submenu"] instanceof ContextMenu
|
||||
) {
|
||||
const menu = data["submenu"];
|
||||
menu.root = false;
|
||||
menu.parent = this;
|
||||
|
||||
const openSubItems = () => {
|
||||
if (data.hasOwnProperty("disabled") && data["disabled"] == true) return;
|
||||
|
||||
this.hideSubMenus();
|
||||
|
||||
const x = this.dom.offsetLeft + this.dom.clientWidth + item.offsetLeft;
|
||||
const y = this.dom.offsetTop + item.offsetTop;
|
||||
|
||||
if (!menu.shown) {
|
||||
menu.show(x, y);
|
||||
} else {
|
||||
menu.hide();
|
||||
}
|
||||
};
|
||||
|
||||
this.submenus.push(menu);
|
||||
|
||||
item.classList.add("has-subitems");
|
||||
item.addEventListener("click", openSubItems);
|
||||
item.addEventListener("mousemove", openSubItems);
|
||||
} else {
|
||||
item.addEventListener("click", () => {
|
||||
this.hideSubMenus();
|
||||
|
||||
if (item.classList.contains("disabled")) return;
|
||||
|
||||
if (
|
||||
data.hasOwnProperty("onclick") &&
|
||||
typeof data["onclick"] === "function"
|
||||
) {
|
||||
const event = {
|
||||
handled: false,
|
||||
item: item,
|
||||
label: label,
|
||||
hotkey: hotkey,
|
||||
items: this.items,
|
||||
data: data
|
||||
};
|
||||
|
||||
data["onclick"](event);
|
||||
|
||||
if (!event.handled) {
|
||||
this.hide();
|
||||
}
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
item.addEventListener("mousemove", () => {
|
||||
this.hideSubMenus();
|
||||
});
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
hideAll() {
|
||||
if (this.root && !this.parent) {
|
||||
if (this.shown) {
|
||||
this.hideSubMenus();
|
||||
|
||||
this.shown = false;
|
||||
this.container.removeChild(this.dom);
|
||||
|
||||
if (this.parent && this.parent.shown) {
|
||||
this.parent.hide();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.parent.hide();
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.dom && this.shown) {
|
||||
this.shown = false;
|
||||
this.hideSubMenus();
|
||||
this.container.removeChild(this.dom);
|
||||
|
||||
if (this.parent && this.parent.shown) {
|
||||
this.parent.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideSubMenus() {
|
||||
for (const menu of this.submenus) {
|
||||
if (menu.shown) {
|
||||
menu.shown = false;
|
||||
menu.container.removeChild(menu.dom);
|
||||
}
|
||||
menu.hideSubMenus();
|
||||
}
|
||||
}
|
||||
|
||||
show(x, y) {
|
||||
this.dom = this.getMenuDom();
|
||||
|
||||
this.dom.style.left = `${x}px`;
|
||||
this.dom.style.top = `${y}px`;
|
||||
|
||||
this.shown = true;
|
||||
this.container.appendChild(this.dom);
|
||||
}
|
||||
|
||||
install() {
|
||||
this.container.addEventListener("contextmenu", this._oncontextmenu);
|
||||
this.container.addEventListener("keydown", this._oncontextmenu_keydown);
|
||||
this.container.addEventListener("click", this._onclick);
|
||||
window.addEventListener("blur", this._onblur);
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
this.items = data;
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
this.dom = null;
|
||||
// this.container.removeEventListener('contextmenu', this._oncontextmenu);
|
||||
this.container.removeEventListener("keydown", this._oncontextmenu_keydown);
|
||||
this.container.removeEventListener("click", this._onclick);
|
||||
window.removeEventListener("blur", this._onblur);
|
||||
}
|
||||
}
|
||||
|
||||
export default ContextMenu;
|
@ -0,0 +1,169 @@
|
||||
import EventEmitter from "events";
|
||||
import hotkeys from "hotkeys-js";
|
||||
import ContextMenu from "./ContextMenu.js";
|
||||
import ServersPlugin from "./ServersPlugin";
|
||||
import { AsyncSeriesHook } from "tapable";
|
||||
|
||||
class Editor extends EventEmitter {
|
||||
canvas: fabric.Canvas;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
contextMenu: any;
|
||||
private pluginMap: {
|
||||
[propName: string]: IPluginTempl;
|
||||
} = {};
|
||||
// 自定义事件
|
||||
private customEvents: string[] = [];
|
||||
// 自定义API
|
||||
private customApis: string[] = [];
|
||||
// 生命周期函数名
|
||||
private hooks: IEditorHooksType[] = [
|
||||
"hookImportBefore",
|
||||
"hookImportAfter",
|
||||
"hookSaveBefore",
|
||||
"hookSaveAfter"
|
||||
];
|
||||
private hooksEntity: {
|
||||
[propName: string]: AsyncSeriesHook;
|
||||
} = {};
|
||||
// constructor(canvas: fabric.Canvas) {
|
||||
// super();
|
||||
// this.canvas = canvas;
|
||||
// this._initContextMenu();
|
||||
// this._bindContextMenu();
|
||||
// this._initActionHooks();
|
||||
// this._initServersPlugin();
|
||||
// }
|
||||
init(canvas: fabric.Canvas) {
|
||||
this.canvas = canvas;
|
||||
this._initContextMenu();
|
||||
this._bindContextMenu();
|
||||
this._initActionHooks();
|
||||
this._initServersPlugin();
|
||||
}
|
||||
|
||||
// 引入组件
|
||||
use(plugin: IPluginClass, options: IPluginOption) {
|
||||
if (this._checkPlugin(plugin)) {
|
||||
this._saveCustomAttr(plugin);
|
||||
const pluginRunTime = new plugin(this.canvas, this, options);
|
||||
this.pluginMap[plugin.pluginName] = pluginRunTime;
|
||||
this._bindingHooks(pluginRunTime);
|
||||
this._bindingHotkeys(pluginRunTime);
|
||||
this._bindingApis(pluginRunTime);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取插件
|
||||
getPlugin(name: string) {
|
||||
if (this.pluginMap[name]) {
|
||||
return this.pluginMap[name];
|
||||
}
|
||||
}
|
||||
|
||||
// 检查组件
|
||||
private _checkPlugin(plugin: IPluginClass) {
|
||||
const { pluginName, events = [], apis = [] } = plugin;
|
||||
//名称检查
|
||||
if (this.pluginMap[pluginName]) {
|
||||
throw new Error(pluginName + "插件重复初始化");
|
||||
}
|
||||
events.forEach((eventName: string) => {
|
||||
if (this.customEvents.find(info => info === eventName)) {
|
||||
throw new Error(pluginName + "插件中" + eventName + "重复");
|
||||
}
|
||||
});
|
||||
|
||||
apis.forEach((apiName: string) => {
|
||||
if (this.customApis.find(info => info === apiName)) {
|
||||
throw new Error(pluginName + "插件中" + apiName + "重复");
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// 绑定hooks方法
|
||||
private _bindingHooks(plugin: IPluginTempl) {
|
||||
this.hooks.forEach(hookName => {
|
||||
const hook = plugin[hookName];
|
||||
if (hook) {
|
||||
this.hooksEntity[hookName].tapPromise(
|
||||
plugin.pluginName + hookName,
|
||||
function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return hook.apply(plugin, [...arguments]);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定快捷键
|
||||
private _bindingHotkeys(plugin: IPluginTempl) {
|
||||
plugin?.hotkeys?.forEach((keyName: string) => {
|
||||
// 支持 keyup
|
||||
hotkeys(keyName, { keyup: true }, e => plugin.hotkeyEvent(keyName, e));
|
||||
});
|
||||
}
|
||||
|
||||
// 保存组件自定义事件与API
|
||||
private _saveCustomAttr(plugin: IPluginClass) {
|
||||
const { events = [], apis = [] } = plugin;
|
||||
this.customApis = this.customApis.concat(apis);
|
||||
this.customEvents = this.customEvents.concat(events);
|
||||
}
|
||||
// 代理API事件
|
||||
private _bindingApis(pluginRunTime: IPluginTempl) {
|
||||
const { apis = [] } = pluginRunTime.constructor;
|
||||
apis.forEach(apiName => {
|
||||
this[apiName] = function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return pluginRunTime[apiName].apply(pluginRunTime, [...arguments]);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 右键菜单
|
||||
private _bindContextMenu() {
|
||||
this.canvas.on("mouse:down", opt => {
|
||||
if (opt.button === 3) {
|
||||
let menu: IPluginMenu[] = [];
|
||||
Object.keys(this.pluginMap).forEach(pluginName => {
|
||||
const pluginRunTime = this.pluginMap[pluginName];
|
||||
const pluginMenu =
|
||||
pluginRunTime.contextMenu && pluginRunTime.contextMenu();
|
||||
if (pluginMenu) {
|
||||
menu = menu.concat(pluginMenu);
|
||||
}
|
||||
});
|
||||
this._renderMenu(opt, menu);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染右键菜单
|
||||
private _renderMenu(opt: fabric.IEvent, menu: IPluginMenu[]) {
|
||||
if (menu.length !== 0) {
|
||||
this.contextMenu.hideAll();
|
||||
this.contextMenu.setData(menu);
|
||||
this.contextMenu.show(opt.e.clientX, opt.e.clientY);
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期事件
|
||||
_initActionHooks() {
|
||||
this.hooks.forEach(hookName => {
|
||||
this.hooksEntity[hookName] = new AsyncSeriesHook(["data"]);
|
||||
});
|
||||
}
|
||||
|
||||
_initContextMenu() {
|
||||
this.contextMenu = new ContextMenu(this.canvas.wrapperEl, []);
|
||||
this.contextMenu.install();
|
||||
}
|
||||
|
||||
_initServersPlugin() {
|
||||
this.use(ServersPlugin, {});
|
||||
}
|
||||
}
|
||||
|
||||
export default Editor;
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-02-03 23:29:34
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:13:16
|
||||
* @Description: 核心入口文件
|
||||
*/
|
||||
import Editor from "./core";
|
||||
import DringPlugin from "./plugin/DringPlugin";
|
||||
import AlignGuidLinePlugin from "./plugin/AlignGuidLinePlugin";
|
||||
import ControlsPlugin from "./plugin/ControlsPlugin";
|
||||
import ControlsRotatePlugin from "./plugin/ControlsRotatePlugin";
|
||||
import CenterAlignPlugin from "./plugin/CenterAlignPlugin";
|
||||
import LayerPlugin from "./plugin/LayerPlugin";
|
||||
import CopyPlugin from "./plugin/CopyPlugin";
|
||||
import MoveHotKeyPlugin from "./plugin/MoveHotKeyPlugin";
|
||||
import DeleteHotKeyPlugin from "./plugin/DeleteHotKeyPlugin";
|
||||
import GroupPlugin from "./plugin/GroupPlugin";
|
||||
import DrawLinePlugin from "./plugin/DrawLinePlugin";
|
||||
import GroupTextEditorPlugin from "./plugin/GroupTextEditorPlugin";
|
||||
import GroupAlignPlugin from "./plugin/GroupAlignPlugin";
|
||||
import WorkspacePlugin from "./plugin/WorkspacePlugin";
|
||||
import DownFontPlugin from "./plugin/DownFontPlugin";
|
||||
import HistoryPlugin from "./plugin/HistoryPlugin";
|
||||
import FlipPlugin from "./plugin/FlipPlugin";
|
||||
import RulerPlugin from "./plugin/RulerPlugin";
|
||||
import MaterialPlugin from "./plugin/MaterialPlugin";
|
||||
|
||||
export {
|
||||
DringPlugin,
|
||||
AlignGuidLinePlugin,
|
||||
ControlsPlugin,
|
||||
ControlsRotatePlugin,
|
||||
CenterAlignPlugin,
|
||||
LayerPlugin,
|
||||
CopyPlugin,
|
||||
MoveHotKeyPlugin,
|
||||
DeleteHotKeyPlugin,
|
||||
GroupPlugin,
|
||||
DrawLinePlugin,
|
||||
GroupTextEditorPlugin,
|
||||
GroupAlignPlugin,
|
||||
WorkspacePlugin,
|
||||
DownFontPlugin,
|
||||
HistoryPlugin,
|
||||
FlipPlugin,
|
||||
RulerPlugin,
|
||||
MaterialPlugin
|
||||
};
|
||||
export default Editor;
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-01-07 01:15:50
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:12:08
|
||||
* @Description: 箭头元素
|
||||
*/
|
||||
import { fabric } from "fabric";
|
||||
|
||||
fabric.Arrow = fabric.util.createClass(fabric.Line, {
|
||||
type: "arrow",
|
||||
superType: "drawing",
|
||||
initialize(points, options) {
|
||||
if (!points) {
|
||||
const { x1, x2, y1, y2 } = options;
|
||||
points = [x1, y1, x2, y2];
|
||||
}
|
||||
options = options || {};
|
||||
this.callSuper("initialize", points, options);
|
||||
},
|
||||
_render(ctx) {
|
||||
this.callSuper("_render", ctx);
|
||||
ctx.save();
|
||||
const xDiff = this.x2 - this.x1;
|
||||
const yDiff = this.y2 - this.y1;
|
||||
const angle = Math.atan2(yDiff, xDiff);
|
||||
ctx.translate((this.x2 - this.x1) / 2, (this.y2 - this.y1) / 2);
|
||||
ctx.rotate(angle);
|
||||
ctx.beginPath();
|
||||
// Move 5px in front of line to start the arrow so it does not have the square line end showing in front (0,0)
|
||||
ctx.moveTo(5, 0);
|
||||
ctx.lineTo(-5, 5);
|
||||
ctx.lineTo(-5, -5);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = this.stroke;
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
fabric.Arrow.fromObject = (options, callback) => {
|
||||
const { x1, x2, y1, y2 } = options;
|
||||
return callback(new fabric.Arrow([x1, y1, x2, y2], options));
|
||||
};
|
||||
|
||||
export default fabric.Arrow;
|
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-05-21 08:55:25
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:13:57
|
||||
* @Description: 辅助线功能
|
||||
*/
|
||||
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
import { fabric } from "fabric";
|
||||
|
||||
declare interface VerticalLine {
|
||||
x: number;
|
||||
y1: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
declare interface HorizontalLine {
|
||||
x1: number;
|
||||
x2: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
class AlignGuidLinePlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
public defautOption = {
|
||||
color: "rgba(255,95,95,1)",
|
||||
width: 1
|
||||
};
|
||||
static pluginName = "AlignGuidLinePlugin";
|
||||
static events = ["", ""];
|
||||
static apis = [];
|
||||
public hotkeys: string[] = [""];
|
||||
dragMode = false;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.dragMode = false;
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
const { canvas } = this;
|
||||
const ctx = canvas.getSelectionContext();
|
||||
const aligningLineOffset = 5;
|
||||
const aligningLineMargin = 4;
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
let viewportTransform: number[] | undefined;
|
||||
let zoom = 1;
|
||||
|
||||
function drawVerticalLine(coords: VerticalLine) {
|
||||
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: HorizontalLine) {
|
||||
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: number, y1: number, x2: number, y2: number) {
|
||||
if (viewportTransform == null) return;
|
||||
|
||||
ctx.save();
|
||||
ctx.lineWidth = self.defautOption.width;
|
||||
ctx.strokeStyle = self.defautOption.color;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(
|
||||
x1 * zoom + viewportTransform[4],
|
||||
y1 * zoom + viewportTransform[5]
|
||||
);
|
||||
ctx.lineTo(
|
||||
x2 * zoom + viewportTransform[4],
|
||||
y2 * zoom + viewportTransform[5]
|
||||
);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function isInRange(value1: number, value2: number) {
|
||||
value1 = Math.round(value1);
|
||||
value2 = Math.round(value2);
|
||||
for (
|
||||
let i = value1 - aligningLineMargin, len = value1 + aligningLineMargin;
|
||||
i <= len;
|
||||
i++
|
||||
) {
|
||||
if (i === value2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const verticalLines: VerticalLine[] = [];
|
||||
const horizontalLines: HorizontalLine[] = [];
|
||||
|
||||
canvas.on("mouse:down", () => {
|
||||
viewportTransform = canvas.viewportTransform;
|
||||
zoom = canvas.getZoom();
|
||||
});
|
||||
|
||||
canvas.on("object:moving", e => {
|
||||
if (viewportTransform === undefined || e.target === undefined) return;
|
||||
|
||||
const activeObject = e.target;
|
||||
const canvasObjects = canvas.getObjects();
|
||||
const activeObjectCenter = activeObject.getCenterPoint();
|
||||
const activeObjectLeft = activeObjectCenter.x;
|
||||
const activeObjectTop = activeObjectCenter.y;
|
||||
const activeObjectBoundingRect = activeObject.getBoundingRect();
|
||||
const activeObjectHeight =
|
||||
activeObjectBoundingRect.height / viewportTransform[3];
|
||||
const activeObjectWidth =
|
||||
activeObjectBoundingRect.width / viewportTransform[0];
|
||||
let horizontalInTheRange = false;
|
||||
let verticalInTheRange = false;
|
||||
const 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 (let i = canvasObjects.length; i--; ) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (canvasObjects[i] === activeObject) continue;
|
||||
|
||||
// 排除辅助线
|
||||
if (
|
||||
activeObject instanceof fabric.GuideLine &&
|
||||
canvasObjects[i] instanceof fabric.GuideLine
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const objectCenter = canvasObjects[i].getCenterPoint();
|
||||
const objectLeft = objectCenter.x;
|
||||
const objectTop = objectCenter.y;
|
||||
const objectBoundingRect = canvasObjects[i].getBoundingRect();
|
||||
const objectHeight = objectBoundingRect.height / viewportTransform[3];
|
||||
const 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", () => {
|
||||
// fix 保存图片时报错
|
||||
try {
|
||||
canvas.clearContext(canvas.contextTop);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on("after:render", () => {
|
||||
for (let i = verticalLines.length; i--; ) {
|
||||
drawVerticalLine(verticalLines[i]);
|
||||
}
|
||||
for (let j = horizontalLines.length; j--; ) {
|
||||
drawHorizontalLine(horizontalLines[j]);
|
||||
}
|
||||
|
||||
// noinspection NestedAssignmentJS
|
||||
verticalLines.length = 0;
|
||||
horizontalLines.length = 0;
|
||||
});
|
||||
|
||||
canvas.on("mouse:up", () => {
|
||||
verticalLines.length = 0;
|
||||
horizontalLines.length = 0;
|
||||
canvas.renderAll();
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default AlignGuidLinePlugin;
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-15 22:49:42
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:14:20
|
||||
* @Description: 居中对齐插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
class CenterAlignPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "CenterAlignPlugin";
|
||||
static apis = ["centerH", "center", "position", "centerV"];
|
||||
// public hotkeys: string[] = ['space'];
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
center(workspace: fabric.Rect, object: fabric.Object) {
|
||||
const center = workspace.getCenterPoint();
|
||||
return this.canvas._centerObject(object, center);
|
||||
}
|
||||
|
||||
centerV(workspace: fabric.Rect, object: fabric.Object) {
|
||||
return this.canvas._centerObject(
|
||||
object,
|
||||
new fabric.Point(object.getCenterPoint().x, workspace.getCenterPoint().y)
|
||||
);
|
||||
}
|
||||
|
||||
centerH(workspace: fabric.Rect, object: fabric.Object) {
|
||||
return this.canvas._centerObject(
|
||||
object,
|
||||
new fabric.Point(workspace.getCenterPoint().x, object.getCenterPoint().y)
|
||||
);
|
||||
}
|
||||
|
||||
position(name: "centerH" | "center" | "centerV") {
|
||||
const anignType = ["centerH", "center", "centerV"];
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
if (anignType.includes(name) && activeObject) {
|
||||
const defaultWorkspace = this.canvas
|
||||
.getObjects()
|
||||
.find(item => item.id === "workspace");
|
||||
if (defaultWorkspace) {
|
||||
console.log(this[name]);
|
||||
this[name](defaultWorkspace, activeObject);
|
||||
}
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu() {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
if (activeObject) {
|
||||
return [
|
||||
{
|
||||
text: "水平垂直居中",
|
||||
hotkey: "Ctrl+V",
|
||||
disabled: false,
|
||||
onclick: () => this.position("center")
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default CenterAlignPlugin;
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-13 23:07:04
|
||||
* @LastEditors: 秦少卫
|
||||
* @LastEditTime: 2023-06-13 23:10:52
|
||||
* @Description: 控制条插件
|
||||
*/
|
||||
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
// 定义旋转光标样式,根据转动角度设定光标旋转
|
||||
function rotateIcon(angle: number) {
|
||||
return `url("data:image/svg+xml,%3Csvg height='18' width='18' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'%3E%3Cg fill='none' transform='rotate(${angle} 16 16)'%3E%3Cpath d='M22.4484 0L32 9.57891L22.4484 19.1478V13.1032C17.6121 13.8563 13.7935 17.6618 13.0479 22.4914H19.2141L9.60201 32.01L0 22.4813H6.54912C7.36524 14.1073 14.0453 7.44023 22.4484 6.61688V0Z' fill='white'/%3E%3Cpath d='M24.0605 3.89587L29.7229 9.57896L24.0605 15.252V11.3562C17.0479 11.4365 11.3753 17.0895 11.3048 24.0879H15.3048L9.60201 29.7308L3.90932 24.0879H8.0806C8.14106 15.3223 15.2645 8.22345 24.0605 8.14313V3.89587Z' fill='black'/%3E%3C/g%3E%3C/svg%3E ") 12 12,crosshair`;
|
||||
}
|
||||
|
||||
class ControlsRotatePlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "ControlsRotatePlugin";
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
const { canvas } = this;
|
||||
// 添加旋转控制响应区域
|
||||
fabric.Object.prototype.controls.mtr = new fabric.Control({
|
||||
x: -0.5,
|
||||
y: -0.5,
|
||||
offsetY: -10,
|
||||
offsetX: -10,
|
||||
rotate: 20,
|
||||
actionName: "rotate",
|
||||
actionHandler: fabric.controlsUtils.rotationWithSnapping,
|
||||
render: () => ""
|
||||
});
|
||||
// ↖左上
|
||||
fabric.Object.prototype.controls.mtr2 = new fabric.Control({
|
||||
x: 0.5,
|
||||
y: -0.5,
|
||||
offsetY: -10,
|
||||
offsetX: 10,
|
||||
rotate: 20,
|
||||
actionName: "rotate",
|
||||
actionHandler: fabric.controlsUtils.rotationWithSnapping,
|
||||
render: () => ""
|
||||
}); // ↗右上
|
||||
fabric.Object.prototype.controls.mtr3 = new fabric.Control({
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
offsetY: 10,
|
||||
offsetX: 10,
|
||||
rotate: 20,
|
||||
actionName: "rotate",
|
||||
actionHandler: fabric.controlsUtils.rotationWithSnapping,
|
||||
render: () => ""
|
||||
}); // ↘右下
|
||||
fabric.Object.prototype.controls.mtr4 = new fabric.Control({
|
||||
x: -0.5,
|
||||
y: 0.5,
|
||||
offsetY: 10,
|
||||
offsetX: -10,
|
||||
rotate: 20,
|
||||
actionName: "rotate",
|
||||
actionHandler: fabric.controlsUtils.rotationWithSnapping,
|
||||
render: () => ""
|
||||
}); // ↙左下
|
||||
|
||||
// 渲染时,执行
|
||||
canvas.on("after:render", () => {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
const angle = activeObj?.angle?.toFixed(2);
|
||||
if (angle !== undefined) {
|
||||
fabric.Object.prototype.controls.mtr.cursorStyle = rotateIcon(
|
||||
Number(angle)
|
||||
);
|
||||
fabric.Object.prototype.controls.mtr2.cursorStyle = rotateIcon(
|
||||
Number(angle) + 90
|
||||
);
|
||||
fabric.Object.prototype.controls.mtr3.cursorStyle = rotateIcon(
|
||||
Number(angle) + 180
|
||||
);
|
||||
fabric.Object.prototype.controls.mtr4.cursorStyle = rotateIcon(
|
||||
Number(angle) + 270
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 旋转时,实时更新旋转控制图标
|
||||
canvas.on("object:rotating", event => {
|
||||
const body = canvas.lowerCanvasEl.nextSibling as HTMLElement;
|
||||
const angle = canvas.getActiveObject()?.angle?.toFixed(2);
|
||||
if (angle === undefined) return;
|
||||
switch (event.transform?.corner) {
|
||||
case "mtr":
|
||||
body.style.cursor = rotateIcon(Number(angle));
|
||||
break;
|
||||
case "mtr2":
|
||||
body.style.cursor = rotateIcon(Number(angle) + 90);
|
||||
break;
|
||||
case "mtr3":
|
||||
body.style.cursor = rotateIcon(Number(angle) + 180);
|
||||
break;
|
||||
case "mtr4":
|
||||
body.style.cursor = rotateIcon(Number(angle) + 270);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // 设置四角旋转光标
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default ControlsRotatePlugin;
|
||||
|
||||
import { fabric } from "fabric";
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-20 12:38:37
|
||||
* @LastEditors: 秦少卫
|
||||
* @LastEditTime: 2023-06-20 13:34:21
|
||||
* @Description: 复制插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
class CopyPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "CopyPlugin";
|
||||
static apis = ["clone"];
|
||||
public hotkeys: string[] = ["ctrl+v", "ctrl+c"];
|
||||
private cache: null | fabric.ActiveSelection | fabric.Object;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
// 多选对象复制
|
||||
_copyActiveSelection(activeObject: fabric.Object) {
|
||||
// 间距设置
|
||||
const grid = 10;
|
||||
const canvas = this.canvas;
|
||||
activeObject?.clone((cloned: fabric.Object) => {
|
||||
// 再次进行克隆,处理选择多个对象的情况
|
||||
cloned.clone((clonedObj: fabric.ActiveSelection) => {
|
||||
canvas.discardActiveObject();
|
||||
if (clonedObj.left === undefined || clonedObj.top === undefined) return;
|
||||
// 将克隆的画布重新赋值
|
||||
clonedObj.canvas = canvas;
|
||||
// 设置位置信息
|
||||
clonedObj.set({
|
||||
left: clonedObj.left + grid,
|
||||
top: clonedObj.top + grid,
|
||||
evented: true,
|
||||
id: uuid()
|
||||
});
|
||||
clonedObj.forEachObject((obj: fabric.Object) => {
|
||||
obj.id = uuid();
|
||||
canvas.add(obj);
|
||||
});
|
||||
// 解决不可选择问题
|
||||
clonedObj.setCoords();
|
||||
canvas.setActiveObject(clonedObj);
|
||||
canvas.requestRenderAll();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 单个对象复制
|
||||
_copyObject(activeObject: fabric.Object) {
|
||||
// 间距设置
|
||||
const grid = 10;
|
||||
const canvas = this.canvas;
|
||||
activeObject?.clone((cloned: fabric.Object) => {
|
||||
if (cloned.left === undefined || cloned.top === undefined) return;
|
||||
canvas.discardActiveObject();
|
||||
// 设置位置信息
|
||||
cloned.set({
|
||||
left: cloned.left + grid,
|
||||
top: cloned.top + grid,
|
||||
evented: true,
|
||||
id: uuid()
|
||||
});
|
||||
canvas.add(cloned);
|
||||
canvas.setActiveObject(cloned);
|
||||
canvas.requestRenderAll();
|
||||
});
|
||||
}
|
||||
|
||||
// 复制元素
|
||||
clone(paramsActiveObeject: fabric.ActiveSelection | fabric.Object) {
|
||||
const activeObject = paramsActiveObeject || this.canvas.getActiveObject();
|
||||
if (!activeObject) return;
|
||||
if (activeObject?.type === "activeSelection") {
|
||||
this._copyActiveSelection(activeObject);
|
||||
} else {
|
||||
this._copyObject(activeObject);
|
||||
}
|
||||
}
|
||||
|
||||
// 快捷键扩展回调
|
||||
hotkeyEvent(eventName: string, e: any) {
|
||||
if (eventName === "ctrl+c" && e.type === "keydown") {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
this.cache = activeObject;
|
||||
}
|
||||
if (eventName === "ctrl+v" && e.type === "keydown") {
|
||||
if (this.cache) {
|
||||
this.clone(this.cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu() {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
if (activeObject) {
|
||||
return [
|
||||
{
|
||||
text: "复制",
|
||||
hotkey: "Ctrl+V",
|
||||
disabled: false,
|
||||
onclick: () => this.clone()
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default CopyPlugin;
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-20 12:57:35
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:14:38
|
||||
* @Description: 删除快捷键
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
// import { v4 as uuid } from 'uuid';
|
||||
|
||||
class DeleteHotKeyPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "DeleteHotKeyPlugin";
|
||||
static apis = ["del"];
|
||||
public hotkeys: string[] = ["backspace"];
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
// 快捷键扩展回调
|
||||
hotkeyEvent(eventName: string, e: any) {
|
||||
if (e.type === "keydown" && eventName === "backspace") {
|
||||
this.del();
|
||||
}
|
||||
}
|
||||
|
||||
del() {
|
||||
const { canvas } = this;
|
||||
const activeObject = canvas.getActiveObjects();
|
||||
if (activeObject) {
|
||||
activeObject.map(item => canvas.remove(item));
|
||||
canvas.requestRenderAll();
|
||||
canvas.discardActiveObject();
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu() {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
if (activeObject) {
|
||||
return [
|
||||
null,
|
||||
{
|
||||
text: "删除",
|
||||
hotkey: "Ctrl+V",
|
||||
disabled: false,
|
||||
onclick: () => this.del()
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default DeleteHotKeyPlugin;
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-27 22:58:57
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:14:43
|
||||
* @Description: 下载字体插件
|
||||
*/
|
||||
|
||||
import { downFontByJSON } from "@/utils/utils";
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
class DownFontPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "DownFontPlugin";
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
hookImportBefore(json) {
|
||||
// console.log(downFontByJSON(json).then, 111);
|
||||
return downFontByJSON(json);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default DownFontPlugin;
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-21 22:09:36
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:14:50
|
||||
* @Description: file content
|
||||
*/
|
||||
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { fabric } from "fabric";
|
||||
import Arrow from "@/core/objects/Arrow";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
class DrawLinePlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "DrawLinePlugin";
|
||||
static apis = ["setArrow", "setMode"];
|
||||
isDrawingLineMode: boolean;
|
||||
isArrow: boolean;
|
||||
lineToDraw: any;
|
||||
pointer: any;
|
||||
pointerPoints: any;
|
||||
isDrawingLine: boolean;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
|
||||
this.isDrawingLine = false;
|
||||
this.isDrawingLineMode = false;
|
||||
this.isArrow = false;
|
||||
this.lineToDraw = null;
|
||||
this.pointer = null;
|
||||
this.pointerPoints = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const { canvas } = this;
|
||||
canvas.on("mouse:down", o => {
|
||||
if (!this.isDrawingLineMode) return;
|
||||
canvas.discardActiveObject();
|
||||
canvas.getObjects().forEach(obj => {
|
||||
obj.selectable = false;
|
||||
obj.hasControls = false;
|
||||
});
|
||||
canvas.requestRenderAll();
|
||||
this.isDrawingLine = true;
|
||||
this.pointer = canvas.getPointer(o.e);
|
||||
this.pointerPoints = [
|
||||
this.pointer.x,
|
||||
this.pointer.y,
|
||||
this.pointer.x,
|
||||
this.pointer.y
|
||||
];
|
||||
|
||||
const NodeHandler = this.isArrow ? Arrow : fabric.Line;
|
||||
this.lineToDraw = new NodeHandler(this.pointerPoints, {
|
||||
strokeWidth: 2,
|
||||
stroke: "#000000",
|
||||
id: uuid()
|
||||
});
|
||||
|
||||
this.lineToDraw.selectable = false;
|
||||
this.lineToDraw.evented = false;
|
||||
this.lineToDraw.strokeUniform = true;
|
||||
canvas.add(this.lineToDraw);
|
||||
});
|
||||
|
||||
canvas.on("mouse:move", o => {
|
||||
if (!this.isDrawingLine) return;
|
||||
canvas.discardActiveObject();
|
||||
const activeObject = canvas.getActiveObject();
|
||||
if (activeObject) return;
|
||||
this.pointer = canvas.getPointer(o.e);
|
||||
|
||||
if (o.e.shiftKey) {
|
||||
// calc angle
|
||||
const startX = this.pointerPoints[0];
|
||||
const startY = this.pointerPoints[1];
|
||||
const x2 = this.pointer.x - startX;
|
||||
const y2 = this.pointer.y - startY;
|
||||
const r = Math.sqrt(x2 * x2 + y2 * y2);
|
||||
let angle = (Math.atan2(y2, x2) / Math.PI) * 180;
|
||||
angle = parseInt(((angle + 7.5) % 360) / 15) * 15;
|
||||
|
||||
const cosx = r * Math.cos((angle * Math.PI) / 180);
|
||||
const sinx = r * Math.sin((angle * Math.PI) / 180);
|
||||
|
||||
this.lineToDraw.set({
|
||||
x2: cosx + startX,
|
||||
y2: sinx + startY
|
||||
});
|
||||
} else {
|
||||
this.lineToDraw.set({
|
||||
x2: this.pointer.x,
|
||||
y2: this.pointer.y
|
||||
});
|
||||
}
|
||||
|
||||
canvas.renderAll();
|
||||
});
|
||||
|
||||
canvas.on("mouse:up", () => {
|
||||
if (!this.isDrawingLine) return;
|
||||
this.lineToDraw.setCoords();
|
||||
this.isDrawingLine = false;
|
||||
canvas.discardActiveObject();
|
||||
});
|
||||
}
|
||||
|
||||
setArrow(params: any) {
|
||||
this.isArrow = params;
|
||||
}
|
||||
|
||||
setMode(params: any) {
|
||||
this.isDrawingLineMode = params;
|
||||
if (!this.isDrawingLineMode) {
|
||||
this.endRest();
|
||||
}
|
||||
}
|
||||
|
||||
endRest() {
|
||||
this.canvas.getObjects().forEach(obj => {
|
||||
if (obj.id !== "workspace") {
|
||||
obj.selectable = true;
|
||||
obj.hasControls = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default DrawLinePlugin;
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-05-19 08:31:34
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:14:55
|
||||
* @Description: 拖拽插件
|
||||
*/
|
||||
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
|
||||
declare type ExtCanvas = fabric.Canvas & {
|
||||
isDragging: boolean;
|
||||
lastPosX: number;
|
||||
lastPosY: number;
|
||||
};
|
||||
|
||||
class DringPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
public defautOption = {};
|
||||
static pluginName = "DringPlugin";
|
||||
static events = ["startDring", "endDring"];
|
||||
static apis = ["startDring", "endDring"];
|
||||
public hotkeys: string[] = ["space"];
|
||||
dragMode = false;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.dragMode = false;
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
this._initDring();
|
||||
}
|
||||
|
||||
startDring() {
|
||||
this.dragMode = true;
|
||||
this.canvas.defaultCursor = "grab";
|
||||
this.editor.emit("startDring");
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
endDring() {
|
||||
this.dragMode = false;
|
||||
this.canvas.defaultCursor = "default";
|
||||
this.canvas.isDragging = false;
|
||||
this.editor.emit("endDring");
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
// 拖拽模式;
|
||||
_initDring() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
this.canvas.on("mouse:down", function (this: ExtCanvas, opt) {
|
||||
const evt = opt.e;
|
||||
if (evt.altKey || self.dragMode) {
|
||||
self.canvas.defaultCursor = "grabbing";
|
||||
self.canvas.discardActiveObject();
|
||||
self._setDring();
|
||||
this.selection = false;
|
||||
this.isDragging = true;
|
||||
this.lastPosX = evt.clientX;
|
||||
this.lastPosY = evt.clientY;
|
||||
this.requestRenderAll();
|
||||
}
|
||||
});
|
||||
|
||||
this.canvas.on("mouse:move", function (this: ExtCanvas, opt) {
|
||||
if (this.isDragging) {
|
||||
self.canvas.discardActiveObject();
|
||||
self.canvas.defaultCursor = "grabbing";
|
||||
const { e } = opt;
|
||||
if (!this.viewportTransform) return;
|
||||
const vpt = this.viewportTransform;
|
||||
vpt[4] += e.clientX - this.lastPosX;
|
||||
vpt[5] += e.clientY - this.lastPosY;
|
||||
this.lastPosX = e.clientX;
|
||||
this.lastPosY = e.clientY;
|
||||
this.requestRenderAll();
|
||||
}
|
||||
});
|
||||
|
||||
this.canvas.on("mouse:up", function (this: ExtCanvas) {
|
||||
if (!this.viewportTransform) return;
|
||||
this.setViewportTransform(this.viewportTransform);
|
||||
this.isDragging = false;
|
||||
this.selection = true;
|
||||
this.getObjects().forEach(obj => {
|
||||
if (obj.id !== "workspace" && obj.hasControls) {
|
||||
obj.selectable = true;
|
||||
}
|
||||
});
|
||||
self.canvas.defaultCursor = "default";
|
||||
this.requestRenderAll();
|
||||
});
|
||||
}
|
||||
|
||||
_setDring() {
|
||||
this.canvas.selection = false;
|
||||
this.canvas.defaultCursor = "grab";
|
||||
this.canvas.getObjects().forEach(obj => {
|
||||
obj.selectable = false;
|
||||
});
|
||||
this.canvas.requestRenderAll();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
|
||||
// 快捷键扩展回调
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
hotkeyEvent(eventName: string, e: any) {
|
||||
if (e.code === "Space" && e.type === "keydown") {
|
||||
if (!this.dragMode) {
|
||||
this.startDring();
|
||||
}
|
||||
}
|
||||
if (e.code === "Space" && e.type === "keyup") {
|
||||
this.endDring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DringPlugin;
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-20 13:21:10
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:29:41
|
||||
* @Description: 组合拆分组合插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
import { v4 as uuid } from "uuid";
|
||||
type IEditor = Editor;
|
||||
|
||||
class GroupPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "GroupPlugin";
|
||||
static apis = ["unGroup", "group"];
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
// 拆分组
|
||||
unGroup() {
|
||||
const activeObject = this.canvas.getActiveObject() as fabric.Group;
|
||||
if (!activeObject) return;
|
||||
// 先获取当前选中的对象,然后打散
|
||||
activeObject.toActiveSelection();
|
||||
activeObject.getObjects().forEach((item: fabric.Object) => {
|
||||
item.set("id", uuid());
|
||||
});
|
||||
this.canvas.discardActiveObject().renderAll();
|
||||
}
|
||||
|
||||
group() {
|
||||
// 组合元素
|
||||
const activeObj = this.canvas.getActiveObject() as fabric.ActiveSelection;
|
||||
if (!activeObj) return;
|
||||
const activegroup = activeObj.toGroup();
|
||||
const objectsInGroup = activegroup.getObjects();
|
||||
activegroup.clone((newgroup: fabric.Group) => {
|
||||
newgroup.set("id", uuid());
|
||||
this.canvas.remove(activegroup);
|
||||
objectsInGroup.forEach(object => {
|
||||
this.canvas.remove(object);
|
||||
});
|
||||
this.canvas.add(newgroup);
|
||||
this.canvas.setActiveObject(newgroup);
|
||||
});
|
||||
}
|
||||
|
||||
contextMenu() {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
console.log(activeObject, "111");
|
||||
if (activeObject && activeObject.type === "group") {
|
||||
return [
|
||||
{
|
||||
text: "拆分组合",
|
||||
hotkey: "Ctrl+V",
|
||||
disabled: false,
|
||||
onclick: () => this.unGroup()
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (this.canvas.getActiveObjects().length > 1) {
|
||||
return [
|
||||
{
|
||||
text: "组合",
|
||||
hotkey: "Ctrl+V",
|
||||
disabled: false,
|
||||
onclick: () => this.group()
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupPlugin;
|
@ -0,0 +1,93 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-20 13:06:31
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:29:49
|
||||
* @Description: 历史记录插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
import { ref } from "vue";
|
||||
import { useRefHistory } from "@vueuse/core";
|
||||
type IEditor = Editor;
|
||||
// import { v4 as uuid } from 'uuid';
|
||||
|
||||
class HistoryPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "HistoryPlugin";
|
||||
static apis = ["undo", "redo", "getHistory"];
|
||||
static events = ["historyInitSuccess"];
|
||||
public hotkeys: string[] = ["ctrl+z"];
|
||||
history: any;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
_init() {
|
||||
this.history = useRefHistory(ref(), {
|
||||
capacity: 50
|
||||
});
|
||||
this.canvas.on({
|
||||
"object:added": event => this._save(event),
|
||||
"object:modified": event => this._save(event),
|
||||
"selection:updated": event => this._save(event)
|
||||
});
|
||||
}
|
||||
|
||||
getHistory() {
|
||||
return this.history;
|
||||
}
|
||||
_save(event) {
|
||||
// 过滤选择元素事件
|
||||
const isSelect = event.action === undefined && event.e;
|
||||
if (isSelect || !this.canvas) return;
|
||||
const workspace = this.canvas
|
||||
.getObjects()
|
||||
.find(item => item.id === "workspace");
|
||||
if (!workspace) {
|
||||
return;
|
||||
}
|
||||
if (this.history.isTracking.value) {
|
||||
this.history.source.value = this.editor.getJson();
|
||||
}
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.history.canUndo.value) {
|
||||
this.renderCanvas();
|
||||
this.history.undo();
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
this.history.redo();
|
||||
this.renderCanvas();
|
||||
}
|
||||
|
||||
renderCanvas = () => {
|
||||
this.history.pause();
|
||||
this.canvas.clear();
|
||||
this.canvas.loadFromJSON(this.history.source.value, () => {
|
||||
this.canvas.renderAll();
|
||||
this.history.resume();
|
||||
});
|
||||
};
|
||||
|
||||
// 快捷键扩展回调
|
||||
hotkeyEvent(eventName: string, e: any) {
|
||||
if (eventName === "ctrl+z" && e.type === "keydown") {
|
||||
this.undo();
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default HistoryPlugin;
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-08-04 21:13:16
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:30:01
|
||||
* @Description: 素材插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
import axios from "axios";
|
||||
|
||||
class MaterialPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "MaterialPlugin";
|
||||
static apis = ["getMaterialType", "getMaterialList"];
|
||||
apiMapUrl: { [propName: string]: string };
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
|
||||
this.apiMapUrl = {
|
||||
template:
|
||||
"https://nihaojob.github.io/vue-fabric-editor-static/template/type.json",
|
||||
svg: "https://nihaojob.github.io/vue-fabric-editor-static/svg/type.json"
|
||||
};
|
||||
}
|
||||
|
||||
// 根据素材类型获取分裂列表
|
||||
async getMaterialType(typeId: string) {
|
||||
const url = this.apiMapUrl[typeId];
|
||||
const res = await axios.get(url, { params: { typeId } });
|
||||
return res.data.data;
|
||||
}
|
||||
|
||||
async getMaterialInfo(typeId: string) {
|
||||
const url = this.apiMapUrl[typeId];
|
||||
const res = await axios.get(url, { params: { typeId } });
|
||||
return res.data.data;
|
||||
}
|
||||
}
|
||||
|
||||
export default MaterialPlugin;
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-20 12:52:09
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:30:04
|
||||
* @Description: 移动快捷键
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
type IEditor = Editor;
|
||||
// import { v4 as uuid } from 'uuid';
|
||||
|
||||
class MoveHotKeyPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "MoveHotKeyPlugin";
|
||||
public hotkeys: string[] = ["left", "right", "down", "up"];
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
// 快捷键扩展回调
|
||||
hotkeyEvent(eventName: string, e: any) {
|
||||
if (e.type === "keydown") {
|
||||
const { canvas } = this;
|
||||
const activeObject = canvas.getActiveObject();
|
||||
if (!activeObject) return;
|
||||
switch (eventName) {
|
||||
case "left":
|
||||
if (activeObject.left === undefined) return;
|
||||
activeObject.set("left", activeObject.left - 1);
|
||||
break;
|
||||
case "right":
|
||||
if (activeObject.left === undefined) return;
|
||||
activeObject.set("left", activeObject.left + 1);
|
||||
break;
|
||||
case "down":
|
||||
if (activeObject.top === undefined) return;
|
||||
activeObject.set("top", activeObject.top + 1);
|
||||
break;
|
||||
case "up":
|
||||
if (activeObject.top === undefined) return;
|
||||
activeObject.set("top", activeObject.top - 1);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
canvas.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default MoveHotKeyPlugin;
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-07-04 23:45:49
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:30:08
|
||||
* @Description: 标尺插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
// import { throttle } from 'lodash-es';
|
||||
type IEditor = Editor;
|
||||
|
||||
import initRuler from "@/core/ruler";
|
||||
|
||||
class RulerPlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "RulerPlugin";
|
||||
// static events = ['sizeChange'];
|
||||
static apis = [
|
||||
"hideGuideline",
|
||||
"showGuideline",
|
||||
"rulerEnable",
|
||||
"rulerDisable"
|
||||
];
|
||||
ruler: any;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.init();
|
||||
}
|
||||
|
||||
hookSaveBefore() {
|
||||
return new Promise(resolve => {
|
||||
this.hideGuideline();
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
hookSaveAfter() {
|
||||
return new Promise(resolve => {
|
||||
this.showGuideline();
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
this.ruler = initRuler(this.canvas);
|
||||
}
|
||||
|
||||
hideGuideline() {
|
||||
this.ruler.hideGuideline();
|
||||
}
|
||||
|
||||
showGuideline() {
|
||||
this.ruler.showGuideline();
|
||||
}
|
||||
|
||||
rulerEnable() {
|
||||
this.ruler.enable();
|
||||
}
|
||||
|
||||
rulerDisable() {
|
||||
this.ruler.disable();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default RulerPlugin;
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-06-27 12:26:41
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:30:13
|
||||
* @Description: 画布区域插件
|
||||
*/
|
||||
|
||||
import { fabric } from "fabric";
|
||||
import Editor from "../core";
|
||||
import { throttle } from "lodash-es";
|
||||
type IEditor = Editor;
|
||||
|
||||
class WorkspacePlugin {
|
||||
public canvas: fabric.Canvas;
|
||||
public editor: IEditor;
|
||||
static pluginName = "WorkspacePlugin";
|
||||
static events = ["sizeChange"];
|
||||
static apis = ["big", "small", "auto", "one", "setSize"];
|
||||
workspaceEl: HTMLElement;
|
||||
workspace: null | fabric.Rect;
|
||||
option: any;
|
||||
constructor(canvas: fabric.Canvas, editor: IEditor) {
|
||||
this.canvas = canvas;
|
||||
this.editor = editor;
|
||||
this.init({
|
||||
width: 900,
|
||||
height: 2000
|
||||
});
|
||||
}
|
||||
|
||||
init(option) {
|
||||
const workspaceEl = document.querySelector("#workspace") as HTMLElement;
|
||||
if (!workspaceEl) {
|
||||
throw new Error("element #workspace is missing, plz check!");
|
||||
}
|
||||
this.workspaceEl = workspaceEl;
|
||||
this.workspace = null;
|
||||
this.option = option;
|
||||
this._initBackground();
|
||||
this._initWorkspace();
|
||||
this._initResizeObserve();
|
||||
this._bindWheel();
|
||||
}
|
||||
|
||||
// hookImportBefore() {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// resolve();
|
||||
// });
|
||||
// }
|
||||
|
||||
hookImportAfter() {
|
||||
return new Promise(resolve => {
|
||||
const workspace = this.canvas
|
||||
.getObjects()
|
||||
.find(item => item.id === "workspace");
|
||||
if (workspace) {
|
||||
workspace.set("selectable", false);
|
||||
workspace.set("hasControls", false);
|
||||
this.setSize(workspace.width, workspace.height);
|
||||
this.editor.emit("sizeChange", workspace.width, workspace.height);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
hookSaveAfter() {
|
||||
return new Promise(resolve => {
|
||||
this.auto();
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化背景
|
||||
_initBackground() {
|
||||
this.canvas.backgroundImage = "";
|
||||
this.canvas.setWidth(this.workspaceEl.offsetWidth);
|
||||
this.canvas.setHeight(this.workspaceEl.offsetHeight);
|
||||
}
|
||||
|
||||
// 初始化画布
|
||||
_initWorkspace() {
|
||||
const { width, height } = this.option;
|
||||
const workspace = new fabric.Rect({
|
||||
fill: "rgba(255,255,255,1)",
|
||||
width,
|
||||
height,
|
||||
id: "workspace",
|
||||
strokeWidth: 0
|
||||
});
|
||||
workspace.set("selectable", false);
|
||||
workspace.set("hasControls", false);
|
||||
workspace.hoverCursor = "default";
|
||||
this.canvas.add(workspace);
|
||||
this.canvas.renderAll();
|
||||
|
||||
this.workspace = workspace;
|
||||
this.auto();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置画布中心到指定对象中心点上
|
||||
* @param {Object} obj 指定的对象
|
||||
*/
|
||||
setCenterFromObject(obj: fabric.Rect) {
|
||||
const { canvas } = this;
|
||||
const objCenter = obj.getCenterPoint();
|
||||
const viewportTransform = canvas.viewportTransform;
|
||||
if (
|
||||
canvas.width === undefined ||
|
||||
canvas.height === undefined ||
|
||||
!viewportTransform
|
||||
)
|
||||
return;
|
||||
viewportTransform[4] =
|
||||
canvas.width / 2 - objCenter.x * viewportTransform[0];
|
||||
viewportTransform[5] =
|
||||
canvas.height / 2 - objCenter.y * viewportTransform[3];
|
||||
canvas.setViewportTransform(viewportTransform);
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
// 初始化监听器
|
||||
_initResizeObserve() {
|
||||
const resizeObserver = new ResizeObserver(
|
||||
throttle(() => {
|
||||
this.auto();
|
||||
}, 50)
|
||||
);
|
||||
resizeObserver.observe(this.workspaceEl);
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this._initBackground();
|
||||
this.option.width = width;
|
||||
this.option.height = height;
|
||||
// 重新设置workspace
|
||||
this.workspace = this.canvas
|
||||
.getObjects()
|
||||
.find(item => item.id === "workspace") as fabric.Rect;
|
||||
this.workspace.set("width", width);
|
||||
this.workspace.set("height", height);
|
||||
this.auto();
|
||||
}
|
||||
|
||||
setZoomAuto(scale: number, cb?: (left?: number, top?: number) => void) {
|
||||
const { workspaceEl } = this;
|
||||
const width = workspaceEl.offsetWidth;
|
||||
const height = workspaceEl.offsetHeight;
|
||||
this.canvas.setWidth(width);
|
||||
this.canvas.setHeight(height);
|
||||
const center = this.canvas.getCenter();
|
||||
this.canvas.setViewportTransform(fabric.iMatrix.concat());
|
||||
this.canvas.zoomToPoint(new fabric.Point(center.left, center.top), scale);
|
||||
if (!this.workspace) return;
|
||||
this.setCenterFromObject(this.workspace);
|
||||
|
||||
// 超出画布不展示
|
||||
this.workspace.clone((cloned: fabric.Rect) => {
|
||||
this.canvas.clipPath = cloned;
|
||||
this.canvas.requestRenderAll();
|
||||
});
|
||||
if (cb) cb(this.workspace.left, this.workspace.top);
|
||||
}
|
||||
|
||||
_getScale() {
|
||||
const viewPortWidth = this.workspaceEl.offsetWidth;
|
||||
const viewPortHeight = this.workspaceEl.offsetHeight;
|
||||
// 按照宽度
|
||||
if (
|
||||
viewPortWidth / viewPortHeight <
|
||||
this.option.width / this.option.height
|
||||
) {
|
||||
return viewPortWidth / this.option.width;
|
||||
} // 按照宽度缩放
|
||||
return viewPortHeight / this.option.height;
|
||||
}
|
||||
|
||||
// 放大
|
||||
big() {
|
||||
let zoomRatio = this.canvas.getZoom();
|
||||
zoomRatio += 0.05;
|
||||
const center = this.canvas.getCenter();
|
||||
this.canvas.zoomToPoint(
|
||||
new fabric.Point(center.left, center.top),
|
||||
zoomRatio
|
||||
);
|
||||
}
|
||||
|
||||
// 缩小
|
||||
small() {
|
||||
let zoomRatio = this.canvas.getZoom();
|
||||
zoomRatio -= 0.05;
|
||||
const center = this.canvas.getCenter();
|
||||
this.canvas.zoomToPoint(
|
||||
new fabric.Point(center.left, center.top),
|
||||
zoomRatio < 0 ? 0.01 : zoomRatio
|
||||
);
|
||||
}
|
||||
|
||||
// 自动缩放
|
||||
auto() {
|
||||
const scale = this._getScale();
|
||||
this.setZoomAuto(scale - 0.08);
|
||||
}
|
||||
|
||||
// 1:1 放大
|
||||
one() {
|
||||
this.setZoomAuto(0.8 - 0.08);
|
||||
this.canvas.requestRenderAll();
|
||||
}
|
||||
|
||||
_bindWheel() {
|
||||
this.canvas.on("mouse:wheel", function (this: fabric.Canvas, opt) {
|
||||
const delta = opt.e.deltaY;
|
||||
let zoom = this.getZoom();
|
||||
zoom *= 0.999 ** delta;
|
||||
if (zoom > 20) zoom = 20;
|
||||
if (zoom < 0.01) zoom = 0.01;
|
||||
const center = this.getCenter();
|
||||
this.zoomToPoint(new fabric.Point(center.left, center.top), zoom);
|
||||
opt.e.preventDefault();
|
||||
opt.e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
console.log("pluginDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkspacePlugin;
|
@ -0,0 +1,129 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { fabric } from "fabric";
|
||||
|
||||
export function setupGuideLine() {
|
||||
if (fabric.GuideLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
fabric.GuideLine = fabric.util.createClass(fabric.Line, {
|
||||
type: "GuideLine",
|
||||
selectable: false,
|
||||
hasControls: false,
|
||||
hasBorders: false,
|
||||
stroke: "#4bec13",
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
padding: 4, // 填充,让辅助线选择范围更大,方便选中
|
||||
globalCompositeOperation: "difference",
|
||||
axis: "horizontal",
|
||||
// excludeFromExport: true,
|
||||
|
||||
initialize(points, options) {
|
||||
const isHorizontal = options.axis === "horizontal";
|
||||
// 指针
|
||||
this.hoverCursor = isHorizontal ? "ns-resize" : "ew-resize";
|
||||
// 设置新的点
|
||||
const newPoints = isHorizontal
|
||||
? [-999999, points, 999999, points]
|
||||
: [points, -999999, points, 999999];
|
||||
// 锁定移动
|
||||
options[isHorizontal ? "lockMovementX" : "lockMovementY"] = true;
|
||||
// 调用父类初始化
|
||||
this.callSuper("initialize", newPoints, options);
|
||||
|
||||
// 绑定事件
|
||||
this.on("mousedown:before", e => {
|
||||
if (this.activeOn === "down") {
|
||||
// 设置selectable:false后激活对象才能进行移动
|
||||
this.canvas.setActiveObject(this, e.e);
|
||||
}
|
||||
});
|
||||
|
||||
this.on("moving", e => {
|
||||
if (this.canvas.ruler.options.enabled && this.isPointOnRuler(e.e)) {
|
||||
this.moveCursor = "not-allowed";
|
||||
} else {
|
||||
this.moveCursor = this.isHorizontal() ? "ns-resize" : "ew-resize";
|
||||
}
|
||||
this.canvas.fire("guideline:moving", {
|
||||
target: this,
|
||||
e: e.e
|
||||
});
|
||||
});
|
||||
|
||||
this.on("mouseup", e => {
|
||||
// 移动到标尺上,移除辅助线
|
||||
if (this.canvas.ruler.options.enabled && this.isPointOnRuler(e.e)) {
|
||||
// console.log('移除辅助线', this);
|
||||
this.canvas.remove(this);
|
||||
return;
|
||||
}
|
||||
this.moveCursor = this.isHorizontal() ? "ns-resize" : "ew-resize";
|
||||
this.canvas.fire("guideline:mouseup", {
|
||||
target: this,
|
||||
e: e.e
|
||||
});
|
||||
});
|
||||
|
||||
this.on("removed", () => {
|
||||
this.off("removed");
|
||||
this.off("mousedown:before");
|
||||
this.off("moving");
|
||||
this.off("mouseup");
|
||||
});
|
||||
},
|
||||
|
||||
getBoundingRect(absolute, calculate) {
|
||||
this.bringToFront();
|
||||
|
||||
const isHorizontal = this.isHorizontal();
|
||||
const rect = this.callSuper("getBoundingRect", absolute, calculate);
|
||||
rect[isHorizontal ? "top" : "left"] +=
|
||||
rect[isHorizontal ? "height" : "width"] / 2;
|
||||
rect[isHorizontal ? "height" : "width"] = 0;
|
||||
return rect;
|
||||
},
|
||||
|
||||
isPointOnRuler(e) {
|
||||
const isHorizontal = this.isHorizontal();
|
||||
const hoveredRuler = this.canvas.ruler.isPointOnRuler(
|
||||
new fabric.Point(e.offsetX, e.offsetY)
|
||||
);
|
||||
if (
|
||||
(isHorizontal && hoveredRuler === "horizontal") ||
|
||||
(!isHorizontal && hoveredRuler === "vertical")
|
||||
) {
|
||||
return hoveredRuler;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isHorizontal() {
|
||||
return this.height === 0;
|
||||
}
|
||||
} as fabric.IGuideLineClassOptions);
|
||||
|
||||
fabric.GuideLine.fromObject = function (object, callback) {
|
||||
const clone = fabric.util.object.clone as (
|
||||
object: any,
|
||||
deep: boolean
|
||||
) => any;
|
||||
|
||||
function _callback(instance: any) {
|
||||
delete instance.xy;
|
||||
callback && callback(instance);
|
||||
}
|
||||
|
||||
const options = clone(object, true);
|
||||
const isHorizontal = options.height === 0;
|
||||
|
||||
options.xy = isHorizontal ? options.y1 : options.x1;
|
||||
options.axis = isHorizontal ? "horizontal" : "vertical";
|
||||
|
||||
fabric.Object._fromObject(options.type, options, _callback, "xy");
|
||||
};
|
||||
}
|
||||
|
||||
export default fabric.GuideLine;
|
@ -0,0 +1,90 @@
|
||||
import type { Canvas } from "fabric/fabric-impl";
|
||||
import { fabric } from "fabric";
|
||||
import CanvasRuler, { RulerOptions } from "./ruler";
|
||||
|
||||
function initRuler(canvas: Canvas, options?: RulerOptions) {
|
||||
const ruler = new CanvasRuler({
|
||||
canvas,
|
||||
...options
|
||||
});
|
||||
|
||||
// 辅助线移动到画板外删除
|
||||
let workspace: fabric.Object | undefined = undefined;
|
||||
|
||||
/**
|
||||
* 获取workspace
|
||||
*/
|
||||
const getWorkspace = () => {
|
||||
workspace = canvas.getObjects().find(item => item.id === "workspace");
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断target是否在object矩形外
|
||||
* @param object
|
||||
* @param target
|
||||
* @returns
|
||||
*/
|
||||
const isRectOut = (
|
||||
object: fabric.Object,
|
||||
target: fabric.GuideLine
|
||||
): boolean => {
|
||||
const { top, height, left, width } = object;
|
||||
|
||||
if (
|
||||
top === undefined ||
|
||||
height === undefined ||
|
||||
left === undefined ||
|
||||
width === undefined
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetRect = target.getBoundingRect(true, true);
|
||||
const {
|
||||
top: targetTop,
|
||||
height: targetHeight,
|
||||
left: targetLeft,
|
||||
width: targetWidth
|
||||
} = targetRect;
|
||||
|
||||
if (
|
||||
target.isHorizontal() &&
|
||||
(top > targetTop + 1 || top + height < targetTop + targetHeight - 1)
|
||||
) {
|
||||
return true;
|
||||
} else if (
|
||||
!target.isHorizontal() &&
|
||||
(left > targetLeft + 1 || left + width < targetLeft + targetWidth - 1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
canvas.on("guideline:moving", e => {
|
||||
if (!workspace) {
|
||||
getWorkspace();
|
||||
return;
|
||||
}
|
||||
const { target } = e;
|
||||
if (isRectOut(workspace, target)) {
|
||||
target.moveCursor = "not-allowed";
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on("guideline:mouseup", e => {
|
||||
if (!workspace) {
|
||||
getWorkspace();
|
||||
return;
|
||||
}
|
||||
const { target } = e;
|
||||
if (isRectOut(workspace, target)) {
|
||||
canvas.remove(target);
|
||||
canvas.setCursor(canvas.defaultCursor ?? "");
|
||||
}
|
||||
});
|
||||
return ruler;
|
||||
}
|
||||
|
||||
export default initRuler;
|
@ -0,0 +1,655 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Canvas, Point, IEvent } from "fabric/fabric-impl";
|
||||
import { fabric } from "fabric";
|
||||
import {
|
||||
getGap,
|
||||
mergeLines,
|
||||
darwRect,
|
||||
darwText,
|
||||
darwLine,
|
||||
drawMask
|
||||
} from "./utils";
|
||||
import { throttle } from "lodash-es";
|
||||
import { setupGuideLine } from "./guideline";
|
||||
|
||||
/**
|
||||
* 配置
|
||||
*/
|
||||
export interface RulerOptions {
|
||||
/**
|
||||
* Canvas
|
||||
*/
|
||||
canvas: Canvas;
|
||||
|
||||
/**
|
||||
* 标尺宽高
|
||||
* @default 20
|
||||
*/
|
||||
ruleSize?: number;
|
||||
|
||||
/**
|
||||
* 字体大小
|
||||
* @default 10
|
||||
*/
|
||||
fontSize?: number;
|
||||
|
||||
/**
|
||||
* 是否开启标尺
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* 背景颜色
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
|
||||
/**
|
||||
* 文字颜色
|
||||
*/
|
||||
textColor?: string;
|
||||
|
||||
/**
|
||||
* 边框颜色
|
||||
*/
|
||||
borderColor?: string;
|
||||
|
||||
/**
|
||||
* 高亮颜色
|
||||
*/
|
||||
highlightColor?: string;
|
||||
}
|
||||
|
||||
export type Rect = { left: number; top: number; width: number; height: number };
|
||||
|
||||
export type HighlightRect = {
|
||||
skip?: "x" | "y";
|
||||
} & Rect;
|
||||
|
||||
class CanvasRuler {
|
||||
protected ctx: CanvasRenderingContext2D;
|
||||
|
||||
/**
|
||||
* 配置
|
||||
*/
|
||||
public options: Required<RulerOptions>;
|
||||
|
||||
/**
|
||||
* 标尺起始点
|
||||
*/
|
||||
public startCalibration: undefined | Point;
|
||||
|
||||
private activeOn: "down" | "up" = "up";
|
||||
|
||||
/**
|
||||
* 选取对象矩形坐标
|
||||
*/
|
||||
private objectRect:
|
||||
| undefined
|
||||
| {
|
||||
x: HighlightRect[];
|
||||
y: HighlightRect[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 事件句柄缓存
|
||||
*/
|
||||
private eventHandler: Record<string, (...args: any) => void> = {
|
||||
// calcCalibration: this.calcCalibration.bind(this),
|
||||
calcObjectRect: throttle(this.calcObjectRect.bind(this), 15),
|
||||
clearStatus: this.clearStatus.bind(this),
|
||||
canvasMouseDown: this.canvasMouseDown.bind(this),
|
||||
canvasMouseMove: throttle(this.canvasMouseMove.bind(this), 15),
|
||||
canvasMouseUp: this.canvasMouseUp.bind(this),
|
||||
render: (e: any) => {
|
||||
// 避免多次渲染
|
||||
if (!e.ctx) return;
|
||||
this.render();
|
||||
}
|
||||
};
|
||||
|
||||
private lastAttr: {
|
||||
status: "out" | "horizontal" | "vertical";
|
||||
cursor: string | undefined;
|
||||
selection: boolean | undefined;
|
||||
} = {
|
||||
status: "out",
|
||||
cursor: undefined,
|
||||
selection: undefined
|
||||
};
|
||||
|
||||
private tempGuidelLine: fabric.GuideLine | undefined;
|
||||
|
||||
constructor(_options: RulerOptions) {
|
||||
// 合并默认配置
|
||||
this.options = Object.assign(
|
||||
{
|
||||
ruleSize: 20,
|
||||
fontSize: 10,
|
||||
enabled: false,
|
||||
backgroundColor: "#fff",
|
||||
borderColor: "#ddd",
|
||||
highlightColor: "#007fff",
|
||||
textColor: "#888"
|
||||
},
|
||||
_options
|
||||
);
|
||||
|
||||
this.ctx = this.options.canvas.getContext();
|
||||
|
||||
fabric.util.object.extend(this.options.canvas, {
|
||||
ruler: this
|
||||
});
|
||||
|
||||
setupGuideLine();
|
||||
|
||||
if (this.options.enabled) {
|
||||
this.enable();
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁
|
||||
public destroy() {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除全部辅助线
|
||||
*/
|
||||
public clearGuideline() {
|
||||
this.options.canvas.remove(
|
||||
...this.options.canvas.getObjects(fabric.GuideLine.prototype.type)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示全部辅助线
|
||||
*/
|
||||
public showGuideline() {
|
||||
this.options.canvas
|
||||
.getObjects(fabric.GuideLine.prototype.type)
|
||||
.forEach(guideLine => {
|
||||
guideLine.set("visible", true);
|
||||
});
|
||||
this.options.canvas.renderAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏全部辅助线
|
||||
*/
|
||||
public hideGuideline() {
|
||||
this.options.canvas
|
||||
.getObjects(fabric.GuideLine.prototype.type)
|
||||
.forEach(guideLine => {
|
||||
guideLine.set("visible", false);
|
||||
});
|
||||
this.options.canvas.renderAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用
|
||||
*/
|
||||
public enable() {
|
||||
this.options.enabled = true;
|
||||
|
||||
// 绑定事件
|
||||
this.options.canvas.on("after:render", this.eventHandler.calcObjectRect);
|
||||
this.options.canvas.on("after:render", this.eventHandler.render);
|
||||
this.options.canvas.on("mouse:down", this.eventHandler.canvasMouseDown);
|
||||
this.options.canvas.on("mouse:move", this.eventHandler.canvasMouseMove);
|
||||
this.options.canvas.on("mouse:up", this.eventHandler.canvasMouseUp);
|
||||
this.options.canvas.on("selection:cleared", this.eventHandler.clearStatus);
|
||||
|
||||
// 显示辅助线
|
||||
this.showGuideline();
|
||||
|
||||
// 绘制一次
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用
|
||||
*/
|
||||
public disable() {
|
||||
// 解除事件
|
||||
this.options.canvas.off("after:render", this.eventHandler.calcObjectRect);
|
||||
this.options.canvas.off("after:render", this.eventHandler.render);
|
||||
this.options.canvas.off("mouse:down", this.eventHandler.canvasMouseDown);
|
||||
this.options.canvas.off("mouse:move", this.eventHandler.canvasMouseMove);
|
||||
this.options.canvas.off("mouse:up", this.eventHandler.canvasMouseUp);
|
||||
this.options.canvas.off("selection:cleared", this.eventHandler.clearStatus);
|
||||
|
||||
// 隐藏辅助线
|
||||
this.hideGuideline();
|
||||
|
||||
this.options.enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制
|
||||
*/
|
||||
public render() {
|
||||
// if (!this.options.enabled) return;
|
||||
const vpt = this.options.canvas.viewportTransform;
|
||||
if (!vpt) return;
|
||||
// 绘制尺子
|
||||
this.draw({
|
||||
isHorizontal: true,
|
||||
rulerLength: this.getSize().width,
|
||||
// startCalibration: -(vpt[4] / vpt[0]),
|
||||
startCalibration: this.startCalibration?.x
|
||||
? this.startCalibration.x
|
||||
: -(vpt[4] / vpt[0])
|
||||
});
|
||||
this.draw({
|
||||
isHorizontal: false,
|
||||
rulerLength: this.getSize().height,
|
||||
// startCalibration: -(vpt[5] / vpt[3]),
|
||||
startCalibration: this.startCalibration?.y
|
||||
? this.startCalibration.y
|
||||
: -(vpt[5] / vpt[3])
|
||||
});
|
||||
// 绘制左上角的遮罩
|
||||
drawMask(this.ctx, {
|
||||
isHorizontal: true,
|
||||
left: -10,
|
||||
top: -10,
|
||||
width: this.options.ruleSize * 2 + 10,
|
||||
height: this.options.ruleSize + 10,
|
||||
backgroundColor: this.options.backgroundColor
|
||||
});
|
||||
drawMask(this.ctx, {
|
||||
isHorizontal: false,
|
||||
left: -10,
|
||||
top: -10,
|
||||
width: this.options.ruleSize + 10,
|
||||
height: this.options.ruleSize * 2 + 10,
|
||||
backgroundColor: this.options.backgroundColor
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取画板尺寸
|
||||
*/
|
||||
private getSize() {
|
||||
return {
|
||||
width: this.options.canvas.width ?? 0,
|
||||
height: this.options.canvas.height ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
private getZoom() {
|
||||
return this.options.canvas.getZoom();
|
||||
}
|
||||
|
||||
private draw(opt: {
|
||||
isHorizontal: boolean;
|
||||
rulerLength: number;
|
||||
startCalibration: number;
|
||||
}) {
|
||||
const { isHorizontal, rulerLength, startCalibration } = opt;
|
||||
const zoom = this.getZoom();
|
||||
|
||||
const gap = getGap(zoom);
|
||||
const unitLength = rulerLength / zoom;
|
||||
const startValue =
|
||||
Math[startCalibration > 0 ? "floor" : "ceil"](startCalibration / gap) *
|
||||
gap;
|
||||
const startOffset = startValue - startCalibration;
|
||||
|
||||
// 标尺背景
|
||||
const canvasSize = this.getSize();
|
||||
darwRect(this.ctx, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: isHorizontal ? canvasSize.width : this.options.ruleSize,
|
||||
height: isHorizontal ? this.options.ruleSize : canvasSize.height,
|
||||
fill: this.options.backgroundColor,
|
||||
stroke: this.options.borderColor
|
||||
});
|
||||
|
||||
// 颜色
|
||||
const textColor = new fabric.Color(this.options.textColor);
|
||||
// 标尺文字显示
|
||||
for (let i = 0; i + startOffset <= Math.ceil(unitLength); i += gap) {
|
||||
const position = (startOffset + i) * zoom;
|
||||
const textValue = startValue + i + "";
|
||||
const textLength = (10 * textValue.length) / 4;
|
||||
const textX = isHorizontal
|
||||
? position - textLength - 1
|
||||
: this.options.ruleSize / 2 - this.options.fontSize / 2 - 4;
|
||||
const textY = isHorizontal
|
||||
? this.options.ruleSize / 2 - this.options.fontSize / 2 - 4
|
||||
: position + textLength;
|
||||
darwText(this.ctx, {
|
||||
text: textValue,
|
||||
left: textX,
|
||||
top: textY,
|
||||
fill: textColor.toRgb(),
|
||||
angle: isHorizontal ? 0 : -90
|
||||
});
|
||||
}
|
||||
|
||||
// 标尺刻度线显示
|
||||
for (let j = 0; j + startOffset <= Math.ceil(unitLength); j += gap) {
|
||||
const position = Math.round((startOffset + j) * zoom);
|
||||
const left = isHorizontal ? position : this.options.ruleSize - 8;
|
||||
const top = isHorizontal ? this.options.ruleSize - 8 : position;
|
||||
const width = isHorizontal ? 0 : 8;
|
||||
const height = isHorizontal ? 8 : 0;
|
||||
darwLine(this.ctx, {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
stroke: textColor.toRgb()
|
||||
});
|
||||
}
|
||||
|
||||
// 标尺蓝色遮罩
|
||||
if (this.objectRect) {
|
||||
const axis = isHorizontal ? "x" : "y";
|
||||
this.objectRect[axis].forEach(rect => {
|
||||
// 跳过指定矩形
|
||||
if (rect.skip === axis) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取数字的值
|
||||
const roundFactor = (x: number) =>
|
||||
Math.round(x / zoom + startCalibration) + "";
|
||||
const leftTextVal = roundFactor(isHorizontal ? rect.left : rect.top);
|
||||
const rightTextVal = roundFactor(
|
||||
isHorizontal ? rect.left + rect.width : rect.top + rect.height
|
||||
);
|
||||
|
||||
const isSameText = leftTextVal === rightTextVal;
|
||||
|
||||
// 背景遮罩
|
||||
const maskOpt = {
|
||||
isHorizontal,
|
||||
width: isHorizontal ? 160 : this.options.ruleSize - 8,
|
||||
height: isHorizontal ? this.options.ruleSize - 8 : 160,
|
||||
backgroundColor: this.options.backgroundColor
|
||||
};
|
||||
drawMask(this.ctx, {
|
||||
...maskOpt,
|
||||
left: isHorizontal ? rect.left - 80 : 0,
|
||||
top: isHorizontal ? 0 : rect.top - 80
|
||||
});
|
||||
if (!isSameText) {
|
||||
drawMask(this.ctx, {
|
||||
...maskOpt,
|
||||
left: isHorizontal ? rect.width + rect.left - 80 : 0,
|
||||
top: isHorizontal ? 0 : rect.height + rect.top - 80
|
||||
});
|
||||
}
|
||||
|
||||
// 颜色
|
||||
const highlightColor = new fabric.Color(this.options.highlightColor);
|
||||
|
||||
// 高亮遮罩
|
||||
highlightColor.setAlpha(0.5);
|
||||
darwRect(this.ctx, {
|
||||
left: isHorizontal ? rect.left : this.options.ruleSize - 8,
|
||||
top: isHorizontal ? this.options.ruleSize - 8 : rect.top,
|
||||
width: isHorizontal ? rect.width : 8,
|
||||
height: isHorizontal ? 8 : rect.height,
|
||||
fill: highlightColor.toRgba()
|
||||
});
|
||||
|
||||
// 两边的数字
|
||||
const pad = this.options.ruleSize / 2 - this.options.fontSize / 2 - 4;
|
||||
|
||||
const textOpt = {
|
||||
fill: highlightColor.toRgba(),
|
||||
angle: isHorizontal ? 0 : -90
|
||||
};
|
||||
|
||||
darwText(this.ctx, {
|
||||
...textOpt,
|
||||
text: leftTextVal,
|
||||
left: isHorizontal ? rect.left - 2 : pad,
|
||||
top: isHorizontal ? pad : rect.top - 2,
|
||||
align: isSameText ? "center" : isHorizontal ? "right" : "left"
|
||||
});
|
||||
|
||||
if (!isSameText) {
|
||||
darwText(this.ctx, {
|
||||
...textOpt,
|
||||
text: rightTextVal,
|
||||
left: isHorizontal ? rect.left + rect.width + 2 : pad,
|
||||
top: isHorizontal ? pad : rect.top + rect.height + 2,
|
||||
align: isHorizontal ? "left" : "right"
|
||||
});
|
||||
}
|
||||
|
||||
// 两边的线
|
||||
const lineSize = isSameText ? 8 : 14;
|
||||
|
||||
highlightColor.setAlpha(1);
|
||||
|
||||
const lineOpt = {
|
||||
width: isHorizontal ? 0 : lineSize,
|
||||
height: isHorizontal ? lineSize : 0,
|
||||
stroke: highlightColor.toRgba()
|
||||
};
|
||||
|
||||
darwLine(this.ctx, {
|
||||
...lineOpt,
|
||||
left: isHorizontal ? rect.left : this.options.ruleSize - lineSize,
|
||||
top: isHorizontal ? this.options.ruleSize - lineSize : rect.top
|
||||
});
|
||||
|
||||
if (!isSameText) {
|
||||
darwLine(this.ctx, {
|
||||
...lineOpt,
|
||||
left: isHorizontal
|
||||
? rect.left + rect.width
|
||||
: this.options.ruleSize - lineSize,
|
||||
top: isHorizontal
|
||||
? this.options.ruleSize - lineSize
|
||||
: rect.top + rect.height
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// draw end
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算起始点
|
||||
*/
|
||||
// private calcCalibration() {
|
||||
// if (this.startCalibration) return;
|
||||
// // console.log('calcCalibration');
|
||||
// const workspace = this.options.canvas.getObjects().find((item: any) => {
|
||||
// return item.id === 'workspace';
|
||||
// });
|
||||
// if (!workspace) return;
|
||||
// const rect = workspace.getBoundingRect(false);
|
||||
// this.startCalibration = new fabric.Point(-rect.left, -rect.top).divide(this.getZoom());
|
||||
// }
|
||||
|
||||
private calcObjectRect() {
|
||||
const activeObjects = this.options.canvas.getActiveObjects();
|
||||
if (activeObjects.length === 0) return;
|
||||
const allRect = activeObjects.reduce((rects, obj) => {
|
||||
const rect: HighlightRect = obj.getBoundingRect(false, true);
|
||||
// 如果是分组单独计算坐标
|
||||
if (obj.group) {
|
||||
const group = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
...obj.group
|
||||
};
|
||||
// 计算矩形坐标
|
||||
rect.width *= group.scaleX;
|
||||
rect.height *= group.scaleY;
|
||||
const groupCenterX = group.width / 2 + group.left;
|
||||
const objectOffsetFromCenterX =
|
||||
(group.width / 2 + (obj.left ?? 0)) * (1 - group.scaleX);
|
||||
rect.left += (groupCenterX - objectOffsetFromCenterX) * this.getZoom();
|
||||
const groupCenterY = group.height / 2 + group.top;
|
||||
const objectOffsetFromCenterY =
|
||||
(group.height / 2 + (obj.top ?? 0)) * (1 - group.scaleY);
|
||||
rect.top += (groupCenterY - objectOffsetFromCenterY) * this.getZoom();
|
||||
}
|
||||
if (obj instanceof fabric.GuideLine) {
|
||||
rect.skip = obj.isHorizontal() ? "x" : "y";
|
||||
}
|
||||
rects.push(rect);
|
||||
return rects;
|
||||
}, [] as HighlightRect[]);
|
||||
if (allRect.length === 0) return;
|
||||
this.objectRect = {
|
||||
x: mergeLines(allRect, true),
|
||||
y: mergeLines(allRect, false)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除起始点和矩形坐标
|
||||
*/
|
||||
private clearStatus() {
|
||||
// this.startCalibration = undefined;
|
||||
this.objectRect = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
判断鼠标是否在标尺上
|
||||
* @param point
|
||||
* @returns "vertical" | "horizontal" | false
|
||||
*/
|
||||
public isPointOnRuler(point: Point) {
|
||||
if (
|
||||
new fabric.Rect({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: this.options.ruleSize,
|
||||
height: this.options.canvas.height
|
||||
}).containsPoint(point)
|
||||
) {
|
||||
return "vertical";
|
||||
} else if (
|
||||
new fabric.Rect({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: this.options.canvas.width,
|
||||
height: this.options.ruleSize
|
||||
}).containsPoint(point)
|
||||
) {
|
||||
return "horizontal";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private canvasMouseDown(e: IEvent<MouseEvent>) {
|
||||
if (!e.pointer || !e.absolutePointer) return;
|
||||
const hoveredRuler = this.isPointOnRuler(e.pointer);
|
||||
if (hoveredRuler && this.activeOn === "up") {
|
||||
// 备份属性
|
||||
this.lastAttr.selection = this.options.canvas.selection;
|
||||
this.options.canvas.selection = false;
|
||||
this.activeOn = "down";
|
||||
|
||||
this.tempGuidelLine = new fabric.GuideLine(
|
||||
hoveredRuler === "horizontal"
|
||||
? e.absolutePointer.y
|
||||
: e.absolutePointer.x,
|
||||
{
|
||||
axis: hoveredRuler,
|
||||
visible: false
|
||||
}
|
||||
);
|
||||
|
||||
this.options.canvas.add(this.tempGuidelLine);
|
||||
this.options.canvas.setActiveObject(this.tempGuidelLine);
|
||||
|
||||
this.options.canvas._setupCurrentTransform(
|
||||
e.e,
|
||||
this.tempGuidelLine,
|
||||
true
|
||||
);
|
||||
|
||||
this.tempGuidelLine.fire("down", this.getCommonEventInfo(e));
|
||||
}
|
||||
}
|
||||
|
||||
private getCommonEventInfo = (e: IEvent<MouseEvent>) => {
|
||||
if (!this.tempGuidelLine || !e.absolutePointer) return;
|
||||
return {
|
||||
e: e.e,
|
||||
transform: this.tempGuidelLine.get("transform"),
|
||||
pointer: {
|
||||
x: e.absolutePointer.x,
|
||||
y: e.absolutePointer.y
|
||||
},
|
||||
target: this.tempGuidelLine
|
||||
};
|
||||
};
|
||||
|
||||
private canvasMouseMove(e: IEvent<MouseEvent>) {
|
||||
if (!e.pointer) return;
|
||||
|
||||
if (this.tempGuidelLine && e.absolutePointer) {
|
||||
const pos: Partial<fabric.IGuideLineOptions> = {};
|
||||
if (this.tempGuidelLine.axis === "horizontal") {
|
||||
pos.top = e.absolutePointer.y;
|
||||
} else {
|
||||
pos.left = e.absolutePointer.x;
|
||||
}
|
||||
this.tempGuidelLine.set({ ...pos, visible: true });
|
||||
|
||||
this.options.canvas.requestRenderAll();
|
||||
|
||||
const event = this.getCommonEventInfo(e);
|
||||
this.options.canvas.fire("object:moving", event);
|
||||
this.tempGuidelLine.fire("moving", event);
|
||||
}
|
||||
|
||||
const hoveredRuler = this.isPointOnRuler(e.pointer);
|
||||
if (!hoveredRuler) {
|
||||
// 鼠标从里面出去
|
||||
if (this.lastAttr.status !== "out") {
|
||||
// 更改鼠标指针
|
||||
this.options.canvas.defaultCursor = this.lastAttr.cursor;
|
||||
this.lastAttr.status = "out";
|
||||
}
|
||||
return;
|
||||
}
|
||||
// const activeObjects = this.options.canvas.getActiveObjects();
|
||||
// if (activeObjects.length === 1 && activeObjects[0] instanceof fabric.GuideLine) {
|
||||
// return;
|
||||
// }
|
||||
// 鼠标从外边进入 或 在另一侧标尺
|
||||
if (
|
||||
this.lastAttr.status === "out" ||
|
||||
hoveredRuler !== this.lastAttr.status
|
||||
) {
|
||||
// 更改鼠标指针
|
||||
this.lastAttr.cursor = this.options.canvas.defaultCursor;
|
||||
this.options.canvas.defaultCursor =
|
||||
hoveredRuler === "horizontal" ? "ns-resize" : "ew-resize";
|
||||
this.lastAttr.status = hoveredRuler;
|
||||
}
|
||||
}
|
||||
|
||||
private canvasMouseUp(e: IEvent<MouseEvent>) {
|
||||
if (this.activeOn !== "down") return;
|
||||
|
||||
// 还原属性
|
||||
this.options.canvas.selection = this.lastAttr.selection;
|
||||
this.activeOn = "up";
|
||||
|
||||
this.tempGuidelLine?.fire("up", this.getCommonEventInfo(e));
|
||||
|
||||
this.tempGuidelLine = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export default CanvasRuler;
|
@ -0,0 +1,62 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type CanvasRuler, { Rect } from "./ruler";
|
||||
|
||||
declare module "fabric/fabric-impl" {
|
||||
type EventNameExt = "removed" | EventName;
|
||||
|
||||
export interface Canvas {
|
||||
_setupCurrentTransform(
|
||||
e: Event,
|
||||
target: fabric.Object,
|
||||
alreadySelected: boolean
|
||||
): void;
|
||||
}
|
||||
|
||||
export interface IObservable<T> {
|
||||
on(
|
||||
eventName: "guideline:moving" | "guideline:mouseup",
|
||||
handler: (event: { e: Event; target: fabric.GuideLine }) => void
|
||||
): T;
|
||||
on(events: {
|
||||
[key: EventName]: (event: { e: Event; target: fabric.GuideLine }) => void;
|
||||
}): T;
|
||||
}
|
||||
|
||||
export interface IGuideLineOptions extends ILineOptions {
|
||||
axis: "horizontal" | "vertical";
|
||||
}
|
||||
|
||||
export interface IGuideLineClassOptions extends IGuideLineOptions {
|
||||
canvas: {
|
||||
setActiveObject(
|
||||
object: fabric.Object | fabric.GuideLine,
|
||||
e?: Event
|
||||
): Canvas;
|
||||
remove<T>(...object: (fabric.Object | fabric.GuideLine)[]): T;
|
||||
} & Canvas;
|
||||
activeOn: "down" | "up";
|
||||
initialize(xy: number, objObjects: IGuideLineOptions): void;
|
||||
callSuper(methodName: string, ...args: unknown[]): any;
|
||||
getBoundingRect(absolute?: boolean, calculate?: boolean): Rect;
|
||||
on(eventName: EventNameExt, handler: (e: IEvent<MouseEvent>) => void): void;
|
||||
off(
|
||||
eventName: EventNameExt,
|
||||
handler?: (e: IEvent<MouseEvent>) => void
|
||||
): void;
|
||||
fire<T>(eventName: EventNameExt, options?: any): T;
|
||||
isPointOnRuler(e: MouseEvent): "horizontal" | "vertical" | false;
|
||||
bringToFront(): fabric.Object;
|
||||
isHorizontal(): boolean;
|
||||
}
|
||||
|
||||
export interface GuideLine extends Line, IGuideLineClassOptions {}
|
||||
|
||||
export class GuideLine extends Line {
|
||||
constructor(xy: number, objObjects?: IGuideLineOptions);
|
||||
static fromObject(object: any, callback: any): void;
|
||||
}
|
||||
|
||||
export interface StaticCanvas {
|
||||
ruler: InstanceType<typeof CanvasRuler>;
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
import type { Rect } from "./ruler";
|
||||
import { fabric } from "fabric";
|
||||
|
||||
/**
|
||||
* 计算尺子间距
|
||||
* @param zoom 缩放比例
|
||||
* @returns 返回计算出的尺子间距
|
||||
*/
|
||||
const getGap = (zoom: number) => {
|
||||
const zooms = [0.02, 0.03, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 18];
|
||||
const gaps = [5000, 2500, 1000, 500, 250, 100, 50, 25, 10, 5, 2];
|
||||
|
||||
let i = 0;
|
||||
while (i < zooms.length && zooms[i] < zoom) {
|
||||
i++;
|
||||
}
|
||||
|
||||
return gaps[i - 1] || 5000;
|
||||
};
|
||||
|
||||
/**
|
||||
* 线段合并
|
||||
* @param rect Rect数组
|
||||
* @param isHorizontal
|
||||
* @returns 合并后的Rect数组
|
||||
*/
|
||||
const mergeLines = (rect: Rect[], isHorizontal: boolean) => {
|
||||
const axis = isHorizontal ? "left" : "top";
|
||||
const length = isHorizontal ? "width" : "height";
|
||||
// 先按照 axis 的大小排序
|
||||
rect.sort((a, b) => a[axis] - b[axis]);
|
||||
const mergedLines = [];
|
||||
let currentLine = Object.assign({}, rect[0]);
|
||||
for (const item of rect) {
|
||||
const line = Object.assign({}, item);
|
||||
if (currentLine[axis] + currentLine[length] >= line[axis]) {
|
||||
// 当前线段和下一个线段相交,合并宽度
|
||||
currentLine[length] =
|
||||
Math.max(
|
||||
currentLine[axis] + currentLine[length],
|
||||
line[axis] + line[length]
|
||||
) - currentLine[axis];
|
||||
} else {
|
||||
// 当前线段和下一个线段不相交,将当前线段加入结果数组中,并更新当前线段为下一个线段
|
||||
mergedLines.push(currentLine);
|
||||
currentLine = Object.assign({}, line);
|
||||
}
|
||||
}
|
||||
// 加入数组
|
||||
mergedLines.push(currentLine);
|
||||
return mergedLines;
|
||||
};
|
||||
|
||||
const darwLine = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
options: {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
stroke?: string | CanvasGradient | CanvasPattern;
|
||||
lineWidth?: number;
|
||||
}
|
||||
) => {
|
||||
ctx.save();
|
||||
const { left, top, width, height, stroke, lineWidth } = options;
|
||||
ctx.beginPath();
|
||||
stroke && (ctx.strokeStyle = stroke);
|
||||
ctx.lineWidth = lineWidth ?? 1;
|
||||
ctx.moveTo(left, top);
|
||||
ctx.lineTo(left + width, top + height);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
const darwText = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
options: {
|
||||
left: number;
|
||||
top: number;
|
||||
text: string;
|
||||
fill?: string | CanvasGradient | CanvasPattern;
|
||||
align?: CanvasTextAlign;
|
||||
angle?: number;
|
||||
fontSize?: number;
|
||||
}
|
||||
) => {
|
||||
ctx.save();
|
||||
const { left, top, text, fill, align, angle, fontSize } = options;
|
||||
fill && (ctx.fillStyle = fill);
|
||||
ctx.textAlign = align ?? "left";
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = `${fontSize ?? 10}px sans-serif`;
|
||||
if (angle) {
|
||||
ctx.translate(left, top);
|
||||
ctx.rotate((Math.PI / 180) * angle);
|
||||
ctx.translate(-left, -top);
|
||||
}
|
||||
ctx.fillText(text, left, top);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
const darwRect = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
options: {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
fill?: string | CanvasGradient | CanvasPattern;
|
||||
stroke?: string;
|
||||
strokeWidth?: number;
|
||||
}
|
||||
) => {
|
||||
ctx.save();
|
||||
const { left, top, width, height, fill, stroke, strokeWidth } = options;
|
||||
ctx.beginPath();
|
||||
fill && (ctx.fillStyle = fill);
|
||||
ctx.rect(left, top, width, height);
|
||||
ctx.fill();
|
||||
if (stroke) {
|
||||
ctx.strokeStyle = stroke;
|
||||
ctx.lineWidth = strokeWidth ?? 1;
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
const drawMask = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
options: {
|
||||
isHorizontal: boolean;
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
backgroundColor: string;
|
||||
}
|
||||
) => {
|
||||
ctx.save();
|
||||
const { isHorizontal, left, top, width, height, backgroundColor } = options;
|
||||
// 创建一个线性渐变对象
|
||||
const gradient = isHorizontal
|
||||
? ctx.createLinearGradient(left, height / 2, left + width, height / 2)
|
||||
: ctx.createLinearGradient(width / 2, top, width / 2, height + top);
|
||||
const transparentColor = new fabric.Color(backgroundColor);
|
||||
transparentColor.setAlpha(0);
|
||||
gradient.addColorStop(0, transparentColor.toRgba());
|
||||
gradient.addColorStop(0.33, backgroundColor);
|
||||
gradient.addColorStop(0.67, backgroundColor);
|
||||
gradient.addColorStop(1, transparentColor.toRgba());
|
||||
darwRect(ctx, {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
fill: gradient
|
||||
});
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
export { getGap, mergeLines, darwRect, darwText, darwLine, drawMask };
|
@ -0,0 +1,8 @@
|
||||
export const customJsonAttr: string[] = [
|
||||
"id",
|
||||
"gradientAngle",
|
||||
"selectable",
|
||||
"hasControls",
|
||||
"userProperty",
|
||||
"animation" // 动画
|
||||
];
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* @Author: zhoux zhouxia@supervision.ltd
|
||||
* @Date: 2023-11-30 16:01:52
|
||||
* @LastEditors: zhoux zhouxia@supervision.ltd
|
||||
* @LastEditTime: 2023-11-30 16:02:47
|
||||
* @FilePath: \vue-fabric-editor\src\hooks\handlers\CustomHandler.ts
|
||||
* @Description: 自定义处理器
|
||||
*/
|
||||
import { Handler } from "./Handler";
|
||||
|
||||
class CustomHandler {
|
||||
handler: Handler;
|
||||
|
||||
constructor(handler: Handler) {
|
||||
this.handler = handler;
|
||||
this.initialze();
|
||||
}
|
||||
|
||||
protected initialze() {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomHandler;
|
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* @Author: zhoux zhouxia@supervision.ltd
|
||||
* @Date: 2023-11-30 15:59:04
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:06:57
|
||||
* @FilePath: \vue-fabric-editor\src\hooks\handlers\Handler.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
|
||||
// import AnimationHandler from './AnimationHandler';
|
||||
import CustomHandler from "./CustomHandler";
|
||||
import { FabricCanvas, FabricObject, FabricObjects } from "./typing";
|
||||
|
||||
export interface HandlerCallback {
|
||||
/**
|
||||
* When has been added object in Canvas, Called function
|
||||
*
|
||||
*/
|
||||
onAdd?: (object: FabricObject) => void;
|
||||
/**
|
||||
* Return contextmenu element
|
||||
*
|
||||
*/
|
||||
// onContext?: (el: HTMLDivElement, e: React.MouseEvent, target?: FabricObject) => Promise<any> | any;
|
||||
/**
|
||||
* Return tooltip element
|
||||
*
|
||||
*/
|
||||
onTooltip?: (el: HTMLDivElement, target?: FabricObject) => Promise<any> | any;
|
||||
/**
|
||||
* When zoom, Called function
|
||||
*/
|
||||
onZoom?: (zoomRatio: number) => void;
|
||||
/**
|
||||
* When clicked object, Called function
|
||||
*
|
||||
*/
|
||||
onClick?: (canvas: FabricCanvas, target: FabricObject) => void;
|
||||
/**
|
||||
* When double clicked object, Called function
|
||||
*
|
||||
*/
|
||||
onDblClick?: (canvas: FabricCanvas, target: FabricObject) => void;
|
||||
/**
|
||||
* When modified object, Called function
|
||||
*/
|
||||
onModified?: (target: FabricObject) => void;
|
||||
/**
|
||||
* When select object, Called function
|
||||
*
|
||||
*/
|
||||
onSelect?: (target: FabricObject) => void;
|
||||
/**
|
||||
* When has been removed object in Canvas, Called function
|
||||
*
|
||||
*/
|
||||
onRemove?: (target: FabricObject) => void;
|
||||
/**
|
||||
* When has been undo or redo, Called function
|
||||
*
|
||||
*/
|
||||
// onTransaction?: (transaction: TransactionEvent) => void;
|
||||
/**
|
||||
* When has been changed interaction mode, Called function
|
||||
*
|
||||
*/
|
||||
// onInteraction?: (interactionMode: InteractionMode) => void;
|
||||
/**
|
||||
* When canvas has been loaded
|
||||
*
|
||||
*/
|
||||
onLoad?: (handler: Handler, canvas?: fabric.Canvas) => void;
|
||||
}
|
||||
|
||||
export interface HandlerOption {
|
||||
/**
|
||||
* Canvas id
|
||||
* @type {string}
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Canvas object
|
||||
* @type {FabricCanvas}
|
||||
*/
|
||||
canvas?: FabricCanvas;
|
||||
/**
|
||||
* Canvas parent element
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
container?: HTMLDivElement;
|
||||
/**
|
||||
* Canvas editable
|
||||
* @type {boolean}
|
||||
*/
|
||||
editable?: boolean;
|
||||
/**
|
||||
* Canvas interaction mode
|
||||
* @type {InteractionMode}
|
||||
*/
|
||||
// interactionMode?: InteractionMode;
|
||||
/**
|
||||
* Persist properties for object
|
||||
* @type {string[]}
|
||||
*/
|
||||
propertiesToInclude?: string[];
|
||||
/**
|
||||
* Minimum zoom ratio
|
||||
* @type {number}
|
||||
*/
|
||||
minZoom?: number;
|
||||
/**
|
||||
* Maximum zoom ratio
|
||||
* @type {number}
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* Zoom ratio step
|
||||
* @type {number}
|
||||
*/
|
||||
zoomStep?: number;
|
||||
/**
|
||||
* Workarea option
|
||||
* @type {WorkareaOption}
|
||||
*/
|
||||
// workareaOption?: WorkareaOption;
|
||||
/**
|
||||
* Canvas option
|
||||
* @type {CanvasOption}
|
||||
*/
|
||||
// canvasOption?: CanvasOption;
|
||||
/**
|
||||
* Grid option
|
||||
* @type {GridOption}
|
||||
*/
|
||||
// gridOption?: GridOption;
|
||||
/**
|
||||
* Default option for Fabric Object
|
||||
* @type {FabricObjectOption}
|
||||
*/
|
||||
// objectOption?: FabricObjectOption;
|
||||
/**
|
||||
* Guideline option
|
||||
* @type {GuidelineOption}
|
||||
*/
|
||||
// guidelineOption?: GuidelineOption;
|
||||
/**
|
||||
* Whether to use zoom
|
||||
* @type {boolean}
|
||||
*/
|
||||
zoomEnabled?: boolean;
|
||||
/**
|
||||
* ActiveSelection option
|
||||
* @type {Partial<FabricObjectOption<fabric.ActiveSelection>>}
|
||||
*/
|
||||
// activeSelectionOption?: Partial<FabricObjectOption<fabric.ActiveSelection>>;
|
||||
/**
|
||||
* Canvas width
|
||||
* @type {number}
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Canvas height
|
||||
* @type {number}
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* Keyboard event in Canvas
|
||||
* @type {KeyEvent}
|
||||
*/
|
||||
// keyEvent?: KeyEvent;
|
||||
/**
|
||||
* Append custom objects
|
||||
* @type {{ [key: string]: any }}
|
||||
*/
|
||||
fabricObjects?: FabricObjects;
|
||||
handlers?: { [key: string]: CustomHandler };
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type HandlerOptions = HandlerOption & HandlerCallback;
|
||||
|
||||
class Handler implements HandlerOptions {
|
||||
public id: string | undefined;
|
||||
public canvas: FabricCanvas | undefined;
|
||||
|
||||
public onAdd?: (object: FabricObject) => void;
|
||||
// public onContext?: (
|
||||
// el: HTMLDivElement,
|
||||
// e: React.MouseEvent,
|
||||
// target?: FabricObject
|
||||
// ) => Promise<any>;
|
||||
public onTooltip?: (
|
||||
el: HTMLDivElement,
|
||||
target?: FabricObject
|
||||
) => Promise<any>;
|
||||
public onZoom?: (zoomRatio: number) => void;
|
||||
public onClick?: (canvas: FabricCanvas, target: FabricObject) => void;
|
||||
public onDblClick?: (canvas: FabricCanvas, target: FabricObject) => void;
|
||||
public onModified?: (target: FabricObject) => void;
|
||||
public onSelect?: (target: FabricObject) => void;
|
||||
public onRemove?: (target: FabricObject) => void;
|
||||
// public onTransaction?: (transaction: TransactionEvent) => void;
|
||||
// public onInteraction?: (interactionMode: InteractionMode) => void;
|
||||
public onLoad?: (handler: Handler, canvas?: fabric.Canvas) => void;
|
||||
|
||||
// public animationHandler!: AnimationHandler;
|
||||
|
||||
constructor(options: HandlerOptions) {
|
||||
this.initialize(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize handler
|
||||
*
|
||||
* @author salgum1114
|
||||
* @param {HandlerOptions} options
|
||||
*/
|
||||
public initialize(options: HandlerOptions) {
|
||||
this.initOption(options);
|
||||
this.initCallback(options);
|
||||
this.initHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init class fields
|
||||
* @param {HandlerOptions} options
|
||||
*/
|
||||
public initOption = (options: HandlerOptions) => {
|
||||
this.id = options.id;
|
||||
this.canvas = options.canvas;
|
||||
// this.container = options.container;
|
||||
// this.editable = options.editable;
|
||||
// this.interactionMode = options.interactionMode;
|
||||
// this.minZoom = options.minZoom;
|
||||
// this.maxZoom = options.maxZoom;
|
||||
// this.zoomStep = options.zoomStep || 0.05;
|
||||
// this.zoomEnabled = options.zoomEnabled;
|
||||
// this.width = options.width;
|
||||
// this.height = options.height;
|
||||
// this.objects = [];
|
||||
// this.setPropertiesToInclude(options.propertiesToInclude);
|
||||
// this.setWorkareaOption(options.workareaOption);
|
||||
// this.setCanvasOption(options.canvasOption);
|
||||
// this.setGridOption(options.gridOption);
|
||||
// this.setObjectOption(options.objectOption);
|
||||
// this.setFabricObjects(options.fabricObjects);
|
||||
// this.setGuidelineOption(options.guidelineOption);
|
||||
// this.setActiveSelectionOption(options.activeSelectionOption);
|
||||
// this.setKeyEvent(options.keyEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize callback
|
||||
* @param {HandlerOptions} options
|
||||
*/
|
||||
public initCallback = (options: HandlerOptions) => {
|
||||
this.onAdd = options.onAdd;
|
||||
this.onTooltip = options.onTooltip;
|
||||
this.onZoom = options.onZoom;
|
||||
// this.onContext = options.onContext;
|
||||
this.onClick = options.onClick;
|
||||
this.onModified = options.onModified;
|
||||
this.onDblClick = options.onDblClick;
|
||||
this.onSelect = options.onSelect;
|
||||
this.onRemove = options.onRemove;
|
||||
// this.onTransaction = options.onTransaction;
|
||||
// this.onInteraction = options.onInteraction;
|
||||
this.onLoad = options.onLoad;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize handlers
|
||||
*
|
||||
*/
|
||||
public initHandler = () => {
|
||||
// this.animationHandler = new AnimationHandler(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find object by id
|
||||
* @param {string} id
|
||||
* @returns {(FabricObject | null)}
|
||||
*/
|
||||
public findById = (id: string): FabricObject | null => {
|
||||
let findObject;
|
||||
const exist = this.objects.some(obj => {
|
||||
if (obj.id === id) {
|
||||
findObject = obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (!exist) {
|
||||
warning(true, "Not found object by id.");
|
||||
return null;
|
||||
}
|
||||
return findObject;
|
||||
};
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
export interface FabricCanvasOption {
|
||||
wrapperEl?: HTMLElement;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
|
||||
export type FabricCanvas<T extends any = fabric.Canvas> = T &
|
||||
FabricCanvasOption;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
|
||||
export type FabricObjectOption<T extends any = fabric.IObjectOptions> = T & {
|
||||
/**
|
||||
* Object id
|
||||
* @type {string}
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Parent object id
|
||||
* @type {string}
|
||||
*/
|
||||
parentId?: string;
|
||||
/**
|
||||
* Original opacity
|
||||
* @type {number}
|
||||
*/
|
||||
originOpacity?: number;
|
||||
/**
|
||||
* Original top position
|
||||
* @type {number}
|
||||
*/
|
||||
originTop?: number;
|
||||
/**
|
||||
* Original left position
|
||||
* @type {number}
|
||||
*/
|
||||
originLeft?: number;
|
||||
/**
|
||||
* Original scale X
|
||||
* @type {number}
|
||||
*/
|
||||
originScaleX?: number;
|
||||
/**
|
||||
* Original scale Y
|
||||
* @type {number}
|
||||
*/
|
||||
originScaleY?: number;
|
||||
/**
|
||||
* Original angle
|
||||
* @type {number}
|
||||
*/
|
||||
originAngle?: number;
|
||||
/**
|
||||
* Original fill color
|
||||
*
|
||||
* @type {(string | fabric.Pattern | fabric.Gradient)}
|
||||
*/
|
||||
originFill?: string | fabric.Pattern | fabric.Gradient;
|
||||
/**
|
||||
* Original stroke color
|
||||
* @type {string}
|
||||
*/
|
||||
originStroke?: string;
|
||||
/**
|
||||
* Original rotation
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
originRotation?: number;
|
||||
/**
|
||||
* Object editable
|
||||
* @type {boolean}
|
||||
*/
|
||||
editable?: boolean;
|
||||
/**
|
||||
* Object Super type
|
||||
* @type {string}
|
||||
*/
|
||||
superType?: string;
|
||||
/**
|
||||
* @description
|
||||
* @type {string}
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Animation property
|
||||
* @type {AnimationProperty}
|
||||
*/
|
||||
animation?: AnimationProperty;
|
||||
/**
|
||||
* Anime instance
|
||||
* @type {anime.AnimeInstance}
|
||||
*/
|
||||
anime?: anime.AnimeInstance;
|
||||
/**
|
||||
* Tooltip property
|
||||
* @type {TooltipProperty}
|
||||
*/
|
||||
tooltip?: TooltipProperty;
|
||||
/**
|
||||
* Link property
|
||||
* @type {LinkProperty}
|
||||
*/
|
||||
link?: LinkProperty;
|
||||
/**
|
||||
* Is running animation
|
||||
* @type {boolean}
|
||||
*/
|
||||
animating?: boolean;
|
||||
/**
|
||||
* Object class
|
||||
* @type {string}
|
||||
*/
|
||||
class?: string;
|
||||
/**
|
||||
* Is possible delete
|
||||
* @type {boolean}
|
||||
*/
|
||||
deletable?: boolean;
|
||||
/**
|
||||
* Is enable double click
|
||||
* @type {boolean}
|
||||
*/
|
||||
dblclick?: boolean;
|
||||
/**
|
||||
* Is possible clone
|
||||
* @type {boolean}
|
||||
*/
|
||||
cloneable?: boolean;
|
||||
/**
|
||||
* Is locked object
|
||||
* @type {boolean}
|
||||
*/
|
||||
locked?: boolean;
|
||||
/**
|
||||
* This property replaces "angle"
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
rotation?: number;
|
||||
/**
|
||||
* Whether it can be clicked
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
clickable?: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
|
||||
export type FabricObject<T extends any = fabric.Object> = T &
|
||||
FabricObjectOption;
|
||||
|
||||
export type FabricObjects = {
|
||||
[key: string]: {
|
||||
create: (...args: any) => FabricObject;
|
||||
};
|
||||
};
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Author: donghao donghao@supervision.ltd
|
||||
* @Date: 2024-08-06 16:50:48
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 17:07:30
|
||||
* @FilePath: \General-AI-Platform-Web-Client\src\hooks\select.ts
|
||||
* @Description: useSelect 类型待优化
|
||||
*/
|
||||
import { inject, onBeforeMount, onMounted, reactive } from "vue";
|
||||
import { SelectEvent, SelectMode, SelectOneType } from "@/utils/event/types";
|
||||
|
||||
interface Selector {
|
||||
mSelectMode: SelectMode;
|
||||
mSelectOneType: SelectOneType;
|
||||
mSelectId: string[] | "";
|
||||
mSelectIds: string[];
|
||||
mSelectActive: unknown[];
|
||||
}
|
||||
|
||||
export default function useSelect() {
|
||||
const state = reactive<Selector>({
|
||||
mSelectMode: SelectMode.EMPTY,
|
||||
mSelectOneType: SelectOneType.EMPTY,
|
||||
mSelectId: "", // 选择id
|
||||
mSelectIds: [], // 选择id
|
||||
mSelectActive: []
|
||||
});
|
||||
|
||||
const fabric = inject("fabric");
|
||||
// const canvas = inject('canvas');
|
||||
const canvasEditor = inject("canvasEditor");
|
||||
const event = inject("event");
|
||||
|
||||
const selectOne = e => {
|
||||
state.mSelectMode = SelectMode.ONE;
|
||||
state.mSelectId = e[0].id;
|
||||
state.mSelectOneType = e[0].type;
|
||||
state.mSelectIds = e.map(item => item.id);
|
||||
};
|
||||
|
||||
const selectMulti = e => {
|
||||
state.mSelectMode = SelectMode.MULTI;
|
||||
state.mSelectId = "";
|
||||
state.mSelectIds = e.map(item => item.id);
|
||||
};
|
||||
|
||||
const selectCancel = () => {
|
||||
state.mSelectId = "";
|
||||
state.mSelectIds = [];
|
||||
state.mSelectMode = SelectMode.EMPTY;
|
||||
state.mSelectOneType = SelectOneType.EMPTY;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
event.on(SelectEvent.ONE, selectOne);
|
||||
event.on(SelectEvent.MULTI, selectMulti);
|
||||
event.on(SelectEvent.CANCEL, selectCancel);
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
event.off(SelectEvent.ONE, selectOne);
|
||||
event.off(SelectEvent.MULTI, selectMulti);
|
||||
event.off(SelectEvent.CANCEL, selectCancel);
|
||||
});
|
||||
|
||||
return {
|
||||
fabric,
|
||||
// canvas,
|
||||
canvasEditor,
|
||||
mixinState: state
|
||||
};
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2022-09-03 19:16:55
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 16:44:50
|
||||
* @Description: 自定义事件
|
||||
*/
|
||||
|
||||
import EventEmitter from "events";
|
||||
import { fabric } from "fabric";
|
||||
import { Canvas } from "fabric/fabric-impl";
|
||||
import { SelectEvent } from "@/utils/event/types";
|
||||
|
||||
/**
|
||||
* 发布订阅器
|
||||
*/
|
||||
class CanvasEventEmitter extends EventEmitter {
|
||||
handler: Canvas | undefined;
|
||||
mSelectMode = "";
|
||||
|
||||
init(handler: CanvasEventEmitter["handler"]) {
|
||||
this.handler = handler;
|
||||
if (this.handler) {
|
||||
this.handler.on("selection:created", () => this.selected());
|
||||
this.handler.on("selection:updated", () => this.selected());
|
||||
this.handler.on("selection:cleared", () => this.selected());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暴露单选多选事件
|
||||
* @private
|
||||
*/
|
||||
private selected() {
|
||||
if (!this.handler) {
|
||||
throw TypeError("还未初始化");
|
||||
}
|
||||
|
||||
const actives = this.handler
|
||||
.getActiveObjects()
|
||||
.filter(item => !(item instanceof fabric.GuideLine)); // 过滤掉辅助线
|
||||
if (actives && actives.length === 1) {
|
||||
this.emit(SelectEvent.ONE, actives);
|
||||
} else if (actives && actives.length > 1) {
|
||||
this.mSelectMode = "multiple";
|
||||
this.emit(SelectEvent.MULTI, actives);
|
||||
} else {
|
||||
this.emit(SelectEvent.CANCEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new CanvasEventEmitter();
|
||||
export { CanvasEventEmitter };
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* @Author: donghao donghao@supervision.ltd
|
||||
* @Date: 2024-08-06 16:44:42
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-06 16:44:55
|
||||
* @FilePath: \General-AI-Platform-Web-Client\src\utils\event\types.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
/*
|
||||
* @Description: 自定义事件的类型
|
||||
*/
|
||||
|
||||
// 选择模式
|
||||
export enum SelectMode {
|
||||
EMPTY = "",
|
||||
ONE = "one",
|
||||
MULTI = "multiple"
|
||||
}
|
||||
|
||||
export enum SelectOneType {
|
||||
EMPTY = "",
|
||||
GROUP = "group",
|
||||
POLYGON = "polygon"
|
||||
}
|
||||
|
||||
// 选择事件(用于广播)
|
||||
export enum SelectEvent {
|
||||
ONE = "selectOne",
|
||||
MULTI = "selectMultiple",
|
||||
CANCEL = "selectCancel"
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* get localStorage 获取本地存储
|
||||
* @param { String } key
|
||||
*/
|
||||
export function getLocal(key: string) {
|
||||
if (!key) throw new Error("key is empty");
|
||||
const value = localStorage.getItem(key);
|
||||
return value ? JSON.parse(value) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* set localStorage 设置本地存储
|
||||
* @param { String } key
|
||||
* @param value
|
||||
*/
|
||||
export function setLocal(key: string, value: unknown) {
|
||||
if (!key) throw new Error("key is empty");
|
||||
if (!value) return;
|
||||
return localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* remove localStorage 移除某个本地存储
|
||||
* @param { String } key
|
||||
*/
|
||||
export function removeLocal(key: string) {
|
||||
if (!key) throw new Error("key is empty");
|
||||
return localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear localStorage 清除本地存储
|
||||
*/
|
||||
export function clearLocal() {
|
||||
return localStorage.clear();
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 获取多边形顶点坐标
|
||||
* @param edges 变数
|
||||
* @param radius 半径
|
||||
* @returns 坐标数组
|
||||
*/
|
||||
const getPolygonVertices = (edges: number, radius: number) => {
|
||||
const vertices = [];
|
||||
const interiorAngle = (Math.PI * 2) / edges;
|
||||
let rotationAdjustment = -Math.PI / 2;
|
||||
if (edges % 2 === 0) {
|
||||
rotationAdjustment += interiorAngle / 2;
|
||||
}
|
||||
for (let i = 0; i < edges; i++) {
|
||||
// 画圆取顶点坐标
|
||||
const rad = i * interiorAngle + rotationAdjustment;
|
||||
vertices.push({
|
||||
x: Math.cos(rad) * radius,
|
||||
y: Math.sin(rad) * radius
|
||||
});
|
||||
}
|
||||
return vertices;
|
||||
};
|
||||
|
||||
export { getPolygonVertices };
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2022-09-05 22:21:55
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-07 16:29:28
|
||||
* @Description: 工具文件
|
||||
*/
|
||||
|
||||
// import FontFaceObserver from "fontfaceobserver";
|
||||
import { useClipboard, useFileDialog, useBase64 } from "@vueuse/core";
|
||||
import { message } from "@/utils/message";
|
||||
|
||||
interface Font {
|
||||
type: string;
|
||||
fontFamily: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 图片文件转字符串
|
||||
* @param {Blob|File} file 文件
|
||||
* @return {String}
|
||||
*/
|
||||
export function getImgStr(file: File | Blob): Promise<FileReader["result"]> {
|
||||
return useBase64(file).promise.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 根据json模板下载字体文件
|
||||
* @param {String} str
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function downFontByJSON(str: string) {
|
||||
// const skipFonts = ["arial", "Microsoft YaHei"];
|
||||
// const fontFamilies: string[] = JSON.parse(str)
|
||||
// .objects.filter(
|
||||
// (item: Font) =>
|
||||
// // 为text 并且不为包含字体
|
||||
// // eslint-disable-next-line implicit-arrow-linebreak
|
||||
// item.type.includes("text") && !skipFonts.includes(item.fontFamily)
|
||||
// )
|
||||
// .map((item: Font) => item.fontFamily);
|
||||
const fontFamiliesAll = [];
|
||||
// TODO 暂时不使用
|
||||
// .map(fontName => {
|
||||
// const font = new FontFaceObserver(fontName);
|
||||
// return font.load(null, 150000);
|
||||
// });
|
||||
return Promise.all(fontFamiliesAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 选择文件
|
||||
* @param {Object} options accept = '', capture = '', multiple = false
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function selectFiles(options: {
|
||||
accept?: string;
|
||||
capture?: string;
|
||||
multiple?: boolean;
|
||||
}): Promise<FileList | null> {
|
||||
return new Promise(resolve => {
|
||||
const { onChange, open } = useFileDialog(options);
|
||||
onChange(files => {
|
||||
resolve(files);
|
||||
});
|
||||
open();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 创建图片元素
|
||||
* @param {String} str 图片地址或者base64图片
|
||||
* @return {Promise} element 图片元素
|
||||
*/
|
||||
export function insertImgFile(str: string) {
|
||||
return new Promise(resolve => {
|
||||
const imgEl = document.createElement("img");
|
||||
imgEl.src = str;
|
||||
// 插入页面
|
||||
document.body.appendChild(imgEl);
|
||||
imgEl.onload = () => {
|
||||
resolve(imgEl);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Copying text to the clipboard
|
||||
* @param source Copy source
|
||||
* @param options Copy options
|
||||
* @returns Promise that resolves when the text is copied successfully, or rejects when the copy fails.
|
||||
*/
|
||||
export const clipboardText = async (
|
||||
source: string,
|
||||
options?: Parameters<typeof useClipboard>[0]
|
||||
) => {
|
||||
try {
|
||||
await useClipboard({ source, ...options }).copy();
|
||||
message("复制成功", { type: "success" });
|
||||
} catch (error) {
|
||||
message("复制失败", { type: "error" });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export function fetchArrayByAttrObject(record: Record<string, any>): any[] {
|
||||
if (!record) {
|
||||
return [
|
||||
{
|
||||
key: "key",
|
||||
value: "value"
|
||||
}
|
||||
];
|
||||
}
|
||||
const finalArr: Record<string, any>[] = [];
|
||||
for (const key in record) {
|
||||
finalArr.push({
|
||||
key,
|
||||
value: record[key]
|
||||
});
|
||||
}
|
||||
return finalArr;
|
||||
}
|
||||
|
||||
export function setAttrObjectByArray(
|
||||
record: Record<string, any>[]
|
||||
): Record<string, any> {
|
||||
if (Array.isArray(record) && record.length && record[0].key != "key1") {
|
||||
const currObj = new Object();
|
||||
record.map((item: Record<string, any>) => {
|
||||
currObj[item?.key || "key"] = item.value;
|
||||
});
|
||||
return currObj;
|
||||
}
|
||||
return {};
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
.deviceSettingAdd_modal_box {
|
||||
.el-form-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
.el-form-item__label {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* @Author: zhoux zhouxia@supervision.ltd
|
||||
* @Date: 2023-12-13 14:29:17
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2024-08-07 11:31:52
|
||||
* @FilePath: \vue-fabric-editor\src\hooks\useAddModels.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
// import {watch00} from '@/assets/modelSettingJsons/watch01'
|
||||
|
||||
import watchError from "@/assets/modelSetting/watchError.svg";
|
||||
import watchOnline from "@/assets/modelSetting/watchOnline.svg";
|
||||
import watchOutline from "@/assets/modelSetting/watchOutline.svg";
|
||||
import watchWarn from "@/assets/modelSetting/watchWarn.svg";
|
||||
import watchErrorSelected from "@/assets/modelSetting/watchErrorSelected.svg";
|
||||
import watchOnlineSelected from "@/assets/modelSetting/watchOnlineSelected.svg";
|
||||
import watchOutlineSelected from "@/assets/modelSetting/watchOutlineSelected.svg";
|
||||
import watchWarnSelected from "@/assets/modelSetting/watchWarnSelected.svg";
|
||||
export interface materialItemI {
|
||||
value: string;
|
||||
name: string;
|
||||
tempUrl: string;
|
||||
src: string;
|
||||
color?: string;
|
||||
}
|
||||
const customObjectJson = {
|
||||
type: "group",
|
||||
objects: [
|
||||
{
|
||||
type: "rect",
|
||||
left: 100,
|
||||
top: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: "red"
|
||||
},
|
||||
{
|
||||
type: "circle",
|
||||
left: 200,
|
||||
top: 100,
|
||||
radius: 50,
|
||||
fill: "blue"
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
left: 150,
|
||||
top: 200,
|
||||
text: "Hello",
|
||||
fontSize: 24,
|
||||
fill: "white"
|
||||
}
|
||||
]
|
||||
};
|
||||
export const useWatchModels = () => {
|
||||
const locaWatchList: materialItemI[] = [
|
||||
{
|
||||
value: "watchError",
|
||||
name: "摄像头1",
|
||||
tempUrl: "",
|
||||
groupObject: customObjectJson,
|
||||
src: watchError,
|
||||
color: "#E80D0D"
|
||||
},
|
||||
{
|
||||
value: "watchOnline",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchOnline,
|
||||
color: "#52C41A"
|
||||
},
|
||||
{
|
||||
value: "watchOutline",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchOutline,
|
||||
color: "#CCCCCC"
|
||||
},
|
||||
{
|
||||
value: "watchWarn",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchWarn,
|
||||
color: "#FAAD14"
|
||||
},
|
||||
{
|
||||
value: "watchErrorSelected",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchErrorSelected,
|
||||
color: "#E80D0D"
|
||||
},
|
||||
{
|
||||
value: "watchOnlineSelected",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchOnlineSelected,
|
||||
color: "#52C41A"
|
||||
},
|
||||
{
|
||||
value: "watchOutlineSelected",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchOutlineSelected,
|
||||
color: "#CCCCCC"
|
||||
},
|
||||
{
|
||||
value: "watchWarnSelected",
|
||||
name: "摄像头",
|
||||
tempUrl: "",
|
||||
src: watchWarnSelected,
|
||||
color: "#FAAD14"
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
locaWatchList
|
||||
};
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
.deviceSetting_wrap {
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
& > header {
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
height: 62px;
|
||||
font-family: Douyin Sans, Douyin Sans;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid rgba(21, 77, 221, 0.2);
|
||||
}
|
||||
|
||||
.main_content {
|
||||
/* background: red; */
|
||||
height: calc(100vh - 160px);
|
||||
.device_add_wrap {
|
||||
width: 57.64vw;
|
||||
margin: 0 auto;
|
||||
padding-top: 40px;
|
||||
.bg_preview {
|
||||
height: 412px;
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO 待使用 */
|
||||
.right-bar {
|
||||
width: 304px;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
}
|
||||
#workspace {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
width: 220px;
|
||||
padding: 10px;
|
||||
padding-top: 0;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
{
|
||||
"version": "5.3.0",
|
||||
"objects": [
|
||||
{
|
||||
"type": "rect",
|
||||
"version": "5.3.0",
|
||||
"originX": "left",
|
||||
"originY": "top",
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": 1200,
|
||||
"height": 900,
|
||||
"fill": "rgba(255,255,255,1)",
|
||||
"stroke": null,
|
||||
"strokeWidth": 0,
|
||||
"strokeDashArray": null,
|
||||
"strokeLineCap": "butt",
|
||||
"strokeDashOffset": 0,
|
||||
"strokeLineJoin": "miter",
|
||||
"strokeUniform": false,
|
||||
"strokeMiterLimit": 4,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"angle": 0,
|
||||
"flipX": false,
|
||||
"flipY": false,
|
||||
"opacity": 1,
|
||||
"shadow": null,
|
||||
"visible": true,
|
||||
"backgroundColor": "",
|
||||
"fillRule": "nonzero",
|
||||
"paintFirst": "fill",
|
||||
"globalCompositeOperation": "source-over",
|
||||
"skewX": 0,
|
||||
"skewY": 0,
|
||||
"rx": 0,
|
||||
"ry": 0,
|
||||
"id": "workspace",
|
||||
"selectable": false,
|
||||
"hasControls": false
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"version": "5.3.0",
|
||||
"originX": "left",
|
||||
"originY": "top",
|
||||
"left": 23.2346,
|
||||
"top": 23.0507,
|
||||
"width": 1600,
|
||||
"height": 1200,
|
||||
"fill": "rgb(0,0,0)",
|
||||
"stroke": null,
|
||||
"strokeWidth": 0,
|
||||
"strokeDashArray": null,
|
||||
"strokeLineCap": "butt",
|
||||
"strokeDashOffset": 0,
|
||||
"strokeLineJoin": "miter",
|
||||
"strokeUniform": false,
|
||||
"strokeMiterLimit": 4,
|
||||
"scaleX": 0.721,
|
||||
"scaleY": 0.721,
|
||||
"angle": 0,
|
||||
"flipX": false,
|
||||
"flipY": false,
|
||||
"opacity": 1,
|
||||
"shadow": null,
|
||||
"visible": true,
|
||||
"backgroundColor": "",
|
||||
"fillRule": "nonzero",
|
||||
"paintFirst": "fill",
|
||||
"globalCompositeOperation": "source-over",
|
||||
"skewX": 0,
|
||||
"skewY": 0,
|
||||
"cropX": 0,
|
||||
"cropY": 0,
|
||||
"id": "a3ab29c6-7008-49fe-abf3-edc9a47cd460",
|
||||
"selectable": false,
|
||||
"hasControls": false,
|
||||
"evented": false,
|
||||
"crossOrigin": null,
|
||||
"src": "https://img.cgmodel.com/image/2020/1010/big/1537169-1390622992.jpg",
|
||||
"filters": []
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 259 KiB |
After Width: | Height: | Size: 393 KiB |
After Width: | Height: | Size: 176 KiB |
@ -0,0 +1,78 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import("vue")["EffectScope"];
|
||||
const computed: typeof import("vue")["computed"];
|
||||
const createApp: typeof import("vue")["createApp"];
|
||||
const customRef: typeof import("vue")["customRef"];
|
||||
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
|
||||
const defineComponent: typeof import("vue")["defineComponent"];
|
||||
const effectScope: typeof import("vue")["effectScope"];
|
||||
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
|
||||
const getCurrentScope: typeof import("vue")["getCurrentScope"];
|
||||
const h: typeof import("vue")["h"];
|
||||
const inject: typeof import("vue")["inject"];
|
||||
const isProxy: typeof import("vue")["isProxy"];
|
||||
const isReactive: typeof import("vue")["isReactive"];
|
||||
const isReadonly: typeof import("vue")["isReadonly"];
|
||||
const isRef: typeof import("vue")["isRef"];
|
||||
const markRaw: typeof import("vue")["markRaw"];
|
||||
const nextTick: typeof import("vue")["nextTick"];
|
||||
const onActivated: typeof import("vue")["onActivated"];
|
||||
const onBeforeMount: typeof import("vue")["onBeforeMount"];
|
||||
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
|
||||
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
|
||||
const onDeactivated: typeof import("vue")["onDeactivated"];
|
||||
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
|
||||
const onMounted: typeof import("vue")["onMounted"];
|
||||
const onRenderTracked: typeof import("vue")["onRenderTracked"];
|
||||
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
|
||||
const onScopeDispose: typeof import("vue")["onScopeDispose"];
|
||||
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
|
||||
const onUnmounted: typeof import("vue")["onUnmounted"];
|
||||
const onUpdated: typeof import("vue")["onUpdated"];
|
||||
const provide: typeof import("vue")["provide"];
|
||||
const reactive: typeof import("vue")["reactive"];
|
||||
const readonly: typeof import("vue")["readonly"];
|
||||
const ref: typeof import("vue")["ref"];
|
||||
const resolveComponent: typeof import("vue")["resolveComponent"];
|
||||
const shallowReactive: typeof import("vue")["shallowReactive"];
|
||||
const shallowReadonly: typeof import("vue")["shallowReadonly"];
|
||||
const shallowRef: typeof import("vue")["shallowRef"];
|
||||
const toRaw: typeof import("vue")["toRaw"];
|
||||
const toRef: typeof import("vue")["toRef"];
|
||||
const toRefs: typeof import("vue")["toRefs"];
|
||||
const toValue: typeof import("vue")["toValue"];
|
||||
const triggerRef: typeof import("vue")["triggerRef"];
|
||||
const unref: typeof import("vue")["unref"];
|
||||
const useAttrs: typeof import("vue")["useAttrs"];
|
||||
const useCssModule: typeof import("vue")["useCssModule"];
|
||||
const useCssVars: typeof import("vue")["useCssVars"];
|
||||
const useSlots: typeof import("vue")["useSlots"];
|
||||
const watch: typeof import("vue")["watch"];
|
||||
const watchEffect: typeof import("vue")["watchEffect"];
|
||||
const watchPostEffect: typeof import("vue")["watchPostEffect"];
|
||||
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type {
|
||||
Component,
|
||||
ComponentPublicInstance,
|
||||
ComputedRef,
|
||||
ExtractDefaultPropTypes,
|
||||
ExtractPropTypes,
|
||||
ExtractPublicPropTypes,
|
||||
InjectionKey,
|
||||
PropType,
|
||||
Ref,
|
||||
VNode,
|
||||
WritableComputedRef
|
||||
} from "vue";
|
||||
import("vue");
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/*
|
||||
* @Author: 秦少卫
|
||||
* @Date: 2023-05-13 18:53:44
|
||||
* @LastEditors: 秦少卫
|
||||
* @LastEditTime: 2023-06-27 22:44:31
|
||||
* @Description: file content
|
||||
*/
|
||||
|
||||
declare interface IPluginOption {
|
||||
[propName: string]: unknown;
|
||||
}
|
||||
|
||||
// 生命周期事件类型
|
||||
declare type IEditorHooksType =
|
||||
| "hookImportBefore"
|
||||
| "hookImportAfter"
|
||||
| "hookSaveBefore"
|
||||
| "hookSaveAfter";
|
||||
|
||||
// 插件class
|
||||
declare interface IPluginClass extends IPluginTempl {
|
||||
new (
|
||||
canvas: fabric.Canvas,
|
||||
editor: any,
|
||||
options: IPluginOption
|
||||
): IPluginTempl;
|
||||
}
|
||||
|
||||
declare interface IPluginMenu {
|
||||
text: string;
|
||||
command?: () => void;
|
||||
child?: IPluginMenu[];
|
||||
}
|
||||
|
||||
// 插件实例
|
||||
declare interface IPluginTempl {
|
||||
pluginName: string;
|
||||
events: string[];
|
||||
apis: string[];
|
||||
canvas: fabric.Canvas;
|
||||
hotkeyEvent: (name: string, e: Event) => viode;
|
||||
[propName: IEditorHooksType]: () => void;
|
||||
[propName: string]: any;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
// import { Object } from 'fabric/fabric-impl';
|
||||
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
declare global {
|
||||
declare module "fabric/fabric-impl" {
|
||||
interface IObjectOptions {
|
||||
/**
|
||||
* 标识
|
||||
*/
|
||||
id?: string | undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export as namespace vfe;
|
||||
declare module "vfe" {
|
||||
export as namespace vfe;
|
||||
export interface ICanvas extends fabric.Canvas {
|
||||
c: fabric.Canvas;
|
||||
editor: Editor;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
declare namespace fabric {
|
||||
export interface Canvas {
|
||||
contextTop: CanvasRenderingContext2D;
|
||||
lowerCanvasEl: HTMLElement;
|
||||
_currentTransform: unknown;
|
||||
_centerObject: (obj: fabric.Object, center: fabric.Point) => fabric.Canvas;
|
||||
}
|
||||
|
||||
export interface Control {
|
||||
rotate: number;
|
||||
}
|
||||
|
||||
function ControlMouseEventHandler(
|
||||
eventData: MouseEvent,
|
||||
transformData: Transform,
|
||||
x: number,
|
||||
y: number
|
||||
): boolean;
|
||||
function ControlStringHandler(
|
||||
eventData: MouseEvent,
|
||||
control: fabric.Control,
|
||||
fabricObject: fabric.Object
|
||||
): string;
|
||||
export const controlsUtils: {
|
||||
rotationWithSnapping: ControlMouseEventHandler;
|
||||
scalingEqually: ControlMouseEventHandler;
|
||||
scalingYOrSkewingX: ControlMouseEventHandler;
|
||||
scalingXOrSkewingY: ControlMouseEventHandler;
|
||||
|
||||
scaleCursorStyleHandler: ControlStringHandler;
|
||||
scaleSkewCursorStyleHandler: ControlStringHandler;
|
||||
scaleOrSkewActionName: ControlStringHandler;
|
||||
rotationStyleHandler: ControlStringHandler;
|
||||
};
|
||||
}
|