import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { fabric } from 'fabric'; import { FabricTexture } from 'fabric-texture'; import Skew from 'fabric-texture/modes/skew' import Perspective from 'fabric-texture/modes/perspective' import Warp from 'fabric-texture/modes/warp' import { Canvas, Code, TextSegment, TextSwitch } from 'docs/components'; import type { CanvasHandlers } from 'docs/components/canvas'; import { DocsContext } from 'docs/docs'; import styles from './style.less'; /** * 分割点计算策略 * @enum {string} */ enum ModeStrategy { /** 扭曲 */ WARP = 'Warp', /** 倾斜 */ SKEW = 'Skew', /** 透视 */ PERSPECTIVE = 'Perspective', } /** * 内置交互模式 */ const modes = { /** 扭曲模式 */ Warp, /** 倾斜模式 */ Skew, /** 透视模式 */ Perspective, }; const InteractiveCanvas = () => { const { placeholder } = useContext(DocsContext); const _canvasRef = useRef(null); const _fabricTextureRef = useRef(); const [mode, setSplitStrategy] = useState(ModeStrategy.PERSPECTIVE); const [showGrid, setShowGrid] = useState(false); const render = useCallback(async () => { const canvas = _canvasRef.current?.getCanvas(); if (!canvas) return; const width = canvas.getWidth(); const height = canvas.getHeight(); if (!placeholder) return; // 缩放以控制在合理渲染区域内 const scale = Math.min(200, canvas.getWidth()) / Math.min(placeholder.naturalWidth, placeholder.naturalHeight); // 创建元素 const object = new fabric.Image(placeholder, { left: width / 2, top: height / 2, scaleX: scale, scaleY: scale, originX: 'center', originY: 'center', }); // 如果已存在则先结束上一轮编辑 if (_fabricTextureRef.current) { _fabricTextureRef.current.leaveEditing(); } // 进入交互态 const fabricTexture = new FabricTexture(canvas, { history: true }); fabricTexture.enterEditing(object, null, new modes[mode]()); _fabricTextureRef.current = fabricTexture; // 隐藏源元素 object.set({ visible: false }); canvas.renderAll(); }, [placeholder]); const handleChangeStrategy = useCallback((mode: ModeStrategy) => { const fabricTexture = _fabricTextureRef.current; if (!fabricTexture) return; const texture = fabricTexture.texture; if (!texture) return; const object = fabricTexture.target!; fabricTexture.leaveEditing(); fabricTexture.enterEditing(object, null, new modes[mode]()); setSplitStrategy(mode as ModeStrategy); }, []); const handleToggleGridShow = useCallback((showGrid: boolean) => { const fabricTexture = _fabricTextureRef.current; if (!fabricTexture) return; const texture = fabricTexture.texture; if (!texture) return; texture.setRenderOptions({ showGrid }); fabricTexture.requestRender(true); setShowGrid(showGrid); }, []); useEffect(() => { render(); }, [render]); return (
{(key) => { switch (key) { case 'mode': return ( void} /> ); case 'show-grid': return ; } }}
); }; export default InteractiveCanvas;