@machete-jhun/canvas-studio 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/render.d.ts CHANGED
@@ -3,17 +3,22 @@ import type { MyShapeConfig } from './types/shapeTypes';
3
3
  export interface RenderOptions {
4
4
  /** 渲染容器 */
5
5
  container: HTMLDivElement;
6
+ /** 背景色,默认透明 */
6
7
  backgroundColor?: string;
7
8
  /** 帧率,默认 30 */
8
9
  fps?: number;
9
10
  /** 变量对象,用于 Rive 动画绑定 */
10
11
  variableObj?: Record<string, unknown>;
12
+ /** 每帧渲染回调 */
13
+ onFrame?: () => void;
11
14
  }
12
15
  export interface RenderResult {
13
16
  layer: Konva.Layer;
14
17
  stage: Konva.Stage;
15
18
  width: number;
16
19
  height: number;
20
+ /** 画布 DOM 元素 */
21
+ canvas: HTMLCanvasElement;
17
22
  /** 更新 Rive 变量 */
18
23
  updateVariables: (variableObj: Record<string, unknown>) => void;
19
24
  /** 销毁资源 */
package/dist/render.js CHANGED
@@ -1 +1 @@
1
- import{m as e}from"./constants-CHIlz4uF.js";import{t as a}from"./types-BH4jm9Jl.js";import{n as o}from"./utils-CqLLOIF1.js";import t from"konva";import{Alignment as n,Fit as r,Layout as i,Rive as c}from"@rive-app/canvas";function s(e){return new Promise((a,o)=>{const t=new window.Image;t.crossOrigin="anonymous",t.onload=()=>a(t),t.onerror=o,t.src=e})}function l(e,a={}){return new Promise((o,t)=>{const{url:s,stateMachine:l,artboard:d,width:u,height:m,inputs:w=[]}=e,h=new OffscreenCanvas(u,m),g=new c({src:s,canvas:h,stateMachines:l,artboard:d,autoplay:!0,autoBind:!0,layout:new i({fit:r.Contain,alignment:n.TopLeft}),onLoad:()=>{g.resizeToCanvas();const e=g.viewModelInstance;e&&w.length>0&&w.forEach(({name:o,type:t,value:n,bindName:r})=>{const i=r?a[r]??n:n;switch(t){case"number":{const a=e.number(o);a&&(a.value=i);break}case"boolean":{const a=e.boolean(o);a&&(a.value=i);break}case"color":{const a=e.color(o);a&&(a.value=i);break}}}),o({rive:g,canvas:h,inputs:w})},onLoadError:e=>{t(/* @__PURE__ */new Error(`Failed to load rive: ${e}`))}})})}async function d(n,r={}){const{type:i,...c}=n;switch(i){case e.RECT:return new t.Rect(c);case e.BACK_SCREEN:{const{width:e,height:a,fill:r,screenId:i}=n,s=new t.Group(c),l=new t.Rect({x:0,y:0,width:e,height:a,fill:r}),d=new t.Text({x:0,y:0,width:e,height:a,text:i,fontSize:e/10,align:"center",verticalAlign:"middle",fill:o(r)});return s.add(l,d),s}case e.TEXT:return new t.Text({...c,wrap:"none",height:void 0,width:void 0});case e.IMAGE:{const e=new t.Image({...c,image:void 0});if(n.imageSource)try{const a=await s(n.imageSource);e.image(a)}catch{console.warn(`Failed to load image: ${n.imageSource}`)}return e}case e.SVG:{const e=new t.Image({...c,image:void 0});if(n.url)try{const a=await s(n.url);e.image(a)}catch{console.warn(`Failed to load svg: ${n.url}`)}return e}case e.QR_CODE:{const e=new t.Image({...c,image:void 0});if(n.info?.svgSource)try{const a=await s(n.info.svgSource);e.image(a)}catch{console.warn("Failed to load qr code")}return e}case e.BAR_CODE:{const e=new t.Image({...c,image:void 0});if(n.info?.svgSource)try{const a=await s(n.info.svgSource);e.image(a)}catch{console.warn("Failed to load bar code")}return e}case e.VIDEO:{const e=n,a=new t.Image({...c,image:void 0});if(e.url)try{const o=await function(e,a,o,t=0){return new Promise((n,r)=>{const i=document.createElement("video");i.crossOrigin="anonymous",i.muted=!0,i.preload="auto",i.onloadeddata=()=>{i.currentTime=t},i.onseeked=()=>{const e=document.createElement("canvas");e.width=a,e.height=o;const t=e.getContext("2d");t&&t.drawImage(i,0,0,a,o),i.pause(),i.src="",i.load(),n(e)},i.onerror=()=>{r(/* @__PURE__ */new Error(`Failed to load video: ${e}`))},i.src=e,i.load()})}(e.url,e.width,e.height,0);a.image(o)}catch{console.warn(`Failed to load video: ${e.url}`)}return a}case e.RIVE:{const e=n,a=new t.Image({...c,image:void 0});if(e.url)try{const{canvas:o}=await l(e,r);a.image(o)}catch{console.warn(`Failed to load rive: ${e.url}`)}return a}default:return a(i),null}}async function u(a,o){const{container:n,backgroundColor:r,fps:i=30,variableObj:c={}}=o,{width:s,height:u}=function(e){let a=0,o=0;for(const t of e){const e=t.x??0,n=t.y??0,r=t.width??0,i=t.height??0,c=t.scaleX??1,s=t.scaleY??1,l=e+r*Math.abs(c),d=n+i*Math.abs(s);l>a&&(a=l),d>o&&(o=d)}return{width:Math.ceil(a)||800,height:Math.ceil(o)||600}}(a),m=new t.Stage({container:n,width:s,height:u}),w=new t.Layer;if(m.add(w),r){const e=new t.Rect({x:0,y:0,width:s,height:u,fill:r});w.add(e)}const h={...c},g=/* @__PURE__ */new Map,v=/* @__PURE__ */new Map;for(const I of a)if(I.type===e.RIVE){const e=I,a=new t.Image({...I,image:void 0});if(e.url)try{const o=await l(e,h);a.image(o.canvas),g.set(I.id,{...o,shape:a})}catch(E){console.warn(`Failed to load rive: ${e.url}`,E)}w.add(a)}else if(I.type===e.VIDEO){const e=I,a=new t.Image({...I,image:void 0});if(e.url)try{const o=document.createElement("video");o.crossOrigin="anonymous",o.muted=!0,o.loop=e.loop??!0,o.playbackRate=e.playbackRate??1,o.src=e.url,await new Promise((e,a)=>{o.onloadeddata=()=>e(),o.onerror=()=>a(),o.load()}),a.image(o),v.set(I.id,{video:o,shape:a})}catch{console.warn(`Failed to load video: ${e.url}`)}w.add(a)}else{const e=await d(I,c);e&&w.add(e)}v.forEach(({video:e})=>{e.play()});let f=null,p=0;const y=1e3/i,b=e=>{const a=e-p;a>=y&&(p=e-a%y,w.batchDraw()),f=requestAnimationFrame(b)};return f=requestAnimationFrame(b),{layer:w,stage:m,width:s,height:u,updateVariables:e=>{g.forEach(({rive:a,inputs:o})=>{const t=a.viewModelInstance;t&&o&&o.forEach(({name:a,type:o,bindName:n})=>{if(!n)return;const r=e[n];if(void 0!==r)switch(o){case"number":{const e=t.number(a);e&&(e.value=r);break}case"boolean":{const e=t.boolean(a);e&&(e.value=r);break}case"color":{const e=t.color(a);e&&(e.value=r);break}}})})},destroy:()=>{null!==f&&(cancelAnimationFrame(f),f=null),g.forEach(({rive:e})=>{e.cleanup()}),g.clear(),v.forEach(({video:e})=>{e.pause(),e.src="",e.load()}),v.clear(),m.destroy()}}}var m=u;export{m as default,u as render};
1
+ import{m as e}from"./constants-CHIlz4uF.js";import{t as a}from"./types-BH4jm9Jl.js";import{n as o}from"./utils-CqLLOIF1.js";import t from"konva";import{Alignment as n,Fit as r,Layout as i,Rive as c}from"@rive-app/canvas";function s(e){return new Promise((a,o)=>{const t=new window.Image;t.crossOrigin="anonymous",t.onload=()=>a(t),t.onerror=o,t.src=e})}function l(e,a={}){return new Promise((o,t)=>{const{url:s,stateMachine:l,artboard:d,width:u,height:m,inputs:w=[]}=e,h=new OffscreenCanvas(u,m),g=new c({src:s,canvas:h,stateMachines:l,artboard:d,autoplay:!0,autoBind:!0,layout:new i({fit:r.Contain,alignment:n.TopLeft}),onLoad:()=>{g.resizeToCanvas();const e=g.viewModelInstance;e&&w.length>0&&w.forEach(({name:o,type:t,value:n,bindName:r})=>{const i=r?a[r]??n:n;switch(t){case"number":{const a=e.number(o);a&&(a.value=i);break}case"boolean":{const a=e.boolean(o);a&&(a.value=i);break}case"color":{const a=e.color(o);a&&(a.value=i);break}}}),o({rive:g,canvas:h,inputs:w})},onLoadError:e=>{t(/* @__PURE__ */new Error(`Failed to load rive: ${e}`))}})})}async function d(n,r={}){const{type:i,...c}=n;switch(i){case e.RECT:return new t.Rect(c);case e.BACK_SCREEN:{const{width:e,height:a,fill:r,screenId:i}=n,s=new t.Group(c),l=new t.Rect({x:0,y:0,width:e,height:a,fill:r}),d=new t.Text({x:0,y:0,width:e,height:a,text:i,fontSize:e/10,align:"center",verticalAlign:"middle",fill:o(r)});return s.add(l,d),s}case e.TEXT:return new t.Text({...c,wrap:"none",height:void 0,width:void 0});case e.IMAGE:{const e=new t.Image({...c,image:void 0});if(n.imageSource)try{const a=await s(n.imageSource);e.image(a)}catch{console.warn(`Failed to load image: ${n.imageSource}`)}return e}case e.SVG:{const e=new t.Image({...c,image:void 0});if(n.url)try{const a=await s(n.url);e.image(a)}catch{console.warn(`Failed to load svg: ${n.url}`)}return e}case e.QR_CODE:{const e=new t.Image({...c,image:void 0});if(n.info?.svgSource)try{const a=await s(n.info.svgSource);e.image(a)}catch{console.warn("Failed to load qr code")}return e}case e.BAR_CODE:{const e=new t.Image({...c,image:void 0});if(n.info?.svgSource)try{const a=await s(n.info.svgSource);e.image(a)}catch{console.warn("Failed to load bar code")}return e}case e.VIDEO:{const e=n,a=new t.Image({...c,image:void 0});if(e.url)try{const o=await function(e,a,o,t=0){return new Promise((n,r)=>{const i=document.createElement("video");i.crossOrigin="anonymous",i.muted=!0,i.preload="auto",i.onloadeddata=()=>{i.currentTime=t},i.onseeked=()=>{const e=document.createElement("canvas");e.width=a,e.height=o;const t=e.getContext("2d");t&&t.drawImage(i,0,0,a,o),i.pause(),i.src="",i.load(),n(e)},i.onerror=()=>{r(/* @__PURE__ */new Error(`Failed to load video: ${e}`))},i.src=e,i.load()})}(e.url,e.width,e.height,0);a.image(o)}catch{console.warn(`Failed to load video: ${e.url}`)}return a}case e.RIVE:{const e=n,a=new t.Image({...c,image:void 0});if(e.url)try{const{canvas:o}=await l(e,r);a.image(o)}catch{console.warn(`Failed to load rive: ${e.url}`)}return a}default:return a(i),null}}async function u(a,o){const{container:n,backgroundColor:r,fps:i=30,variableObj:c={},onFrame:s}=o,u=new t.Stage({container:n,width:100,height:100}),m=new t.Layer;u.add(m);const w={...c},h=/* @__PURE__ */new Map,g=/* @__PURE__ */new Map;for(const R of a)if(R.type===e.RIVE){const e=R,a=new t.Image({...R,image:void 0});if(e.url)try{const o=await l(e,w);a.image(o.canvas),h.set(R.id,{...o,shape:a})}catch(C){console.warn(`Failed to load rive: ${e.url}`,C)}m.add(a)}else if(R.type===e.VIDEO){const e=R,a=new t.Image({...R,image:void 0});if(e.url)try{const o=document.createElement("video");o.crossOrigin="anonymous",o.muted=!0,o.loop=e.loop??!0,o.playbackRate=e.playbackRate??1,o.src=e.url,await new Promise((e,a)=>{o.onloadeddata=()=>e(),o.onerror=()=>a(),o.load()}),a.image(o),g.set(R.id,{video:o,shape:a})}catch{console.warn(`Failed to load video: ${e.url}`)}m.add(a)}else{const e=await d(R,c);e&&m.add(e)}m.batchDraw(),s?.();const v=m.getClientRect({skipTransform:!1}),f=Math.floor(v.x),p=Math.floor(v.y),y=Math.ceil(v.width),b=Math.ceil(v.height);if(u.width(y),u.height(b),m.x(-f),m.y(-p),r){const e=new t.Rect({x:f,y:p,width:y,height:b,fill:r});m.add(e),e.moveToBottom()}g.forEach(({video:e})=>{e.play()});let E=null,I=0;const F=1e3/i,k=e=>{const a=e-I;a>=F&&(I=e-a%F,m.batchDraw(),s?.()),E=requestAnimationFrame(k)};return E=requestAnimationFrame(k),{layer:m,stage:u,width:y,height:b,canvas:m.getNativeCanvasElement(),updateVariables:e=>{h.forEach(({rive:a,inputs:o})=>{const t=a.viewModelInstance;t&&o&&o.forEach(({name:a,type:o,bindName:n})=>{if(!n)return;const r=e[n];if(void 0!==r)switch(o){case"number":{const e=t.number(a);e&&(e.value=r);break}case"boolean":{const e=t.boolean(a);e&&(e.value=r);break}case"color":{const e=t.color(a);e&&(e.value=r);break}}})})},destroy:()=>{null!==E&&(cancelAnimationFrame(E),E=null),h.forEach(({rive:e})=>{e.cleanup()}),h.clear(),g.forEach(({video:e})=>{e.pause(),e.src="",e.load()}),g.clear(),u.destroy()}}}var m=u;export{m as default,u as render};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@machete-jhun/canvas-studio",
3
3
  "description": "一个基于 React 和 Konva 的画布编辑组件库,支持多种图形和媒体元素的添加与编辑,适用于构建复杂的图形应用。",
4
4
  "private": false,
5
- "version": "0.0.11",
5
+ "version": "0.0.13",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "module": "./dist/index.js",
@@ -88,6 +88,8 @@
88
88
  "terser": "^5.46.0",
89
89
  "typescript": "~5.9.3",
90
90
  "typescript-eslint": "^8.53.1",
91
+ "unocss": "^66.6.0",
92
+ "unocss-preset-scalpel": "^1.2.7",
91
93
  "vite": "npm:rolldown-vite@7.2.5"
92
94
  },
93
95
  "overrides": {