import React, { useCallback, useContext, useEffect, useState } from 'react'; import { Texture } 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 cloneDeep from 'lodash-es/cloneDeep'; import { DocsContext } from 'docs/docs'; import { Code, CodePreview, TextSegment, TextValue } from 'docs/components'; import styles from './style.less'; /** * 分割点计算策略 * @enum {string} */ enum ModeStrategy { /** 扭曲 */ WARP = 'Warp', /** 倾斜 */ SKEW = 'Skew', /** 透视 */ PERSPECTIVE = 'Perspective', } /** * 内置分割点计算策略 * @remarks Texture 类默认应用扭曲策略计算分割点 */ const strategies = { /** 扭曲 */ WARP: Warp.execute, /** 倾斜 */ SKEW: Skew.execute, /** 透视 */ PERSPECTIVE: Perspective.execute, }; const Customization = () => { const { placeholder } = useContext(DocsContext); const [preview, setPreview] = useState(); const [mode, setMode] = useState(ModeStrategy.WARP); const [values, setValues] = useState<[x: number, y: number][]>(); const render = useCallback(async () => { if (!placeholder) return; if (!values) return; const canvas = document.createElement('canvas'); canvas.width = placeholder.naturalWidth; canvas.height = placeholder.naturalHeight; const ctx = canvas.getContext('2d'); if (!ctx) return; ctx.drawImage(placeholder, 0, 0); const texture = new Texture(canvas) .setSplitUnit(0.01) .setInputLimitSize({ width: 400 }) .setSplitStrategy({ name: mode, execute: strategies[mode.toUpperCase()], }); switch (mode) { case ModeStrategy.SKEW: texture.setVertexCoord(0, 0, 'tl', { x: values[0][0], y: values[0][1] }); texture.setVertexCoord(0, 0, 'tr', { x: values[1][0], y: values[1][1] }); break; case ModeStrategy.PERSPECTIVE: texture.setVertexCoord(0, 0, 'tl', { x: values[0][0], y: values[0][1] }); texture.setVertexCoord(0, 0, 'tr', { x: values[1][0], y: values[1][1] }); break; case ModeStrategy.WARP: texture.setBoundCoords(0, 0, 'top', [ { x: values[0][0], y: values[0][1] }, { x: values[1][0], y: values[1][1] }, { x: values[2][0], y: values[2][1] }, { x: values[3][0], y: values[3][1] }, ]); break; } setPreview(texture.create().toDataURL()); }, [placeholder, mode, values]); /** 改变数值 */ const changeValue = useCallback((index: number, target: 'x' | 'y') => { return (value: number) => { setValues((pre) => { if (!pre) return pre; const _values = cloneDeep(pre); _values[index][{ x: 0, y: 1 }[target]] = value; return _values; }); }; }, []); useEffect(() => { if (placeholder) { setMode(ModeStrategy.WARP); setValues([ [0, 0], [Math.round(placeholder.width / 3), 0], [Math.round((placeholder.width / 3) * 2), 0], [placeholder.width, 0], ]); } }, [placeholder]); useEffect(() => { render(); }, [render]); return (
{placeholder && preview && } {(key) => { if (key === 'mode-name') { return ( { if (!placeholder) return; const _mode = mode as ModeStrategy; switch (_mode) { case ModeStrategy.SKEW: case ModeStrategy.PERSPECTIVE: setValues([ [0, 0], [placeholder.width, 0], ]); break; case ModeStrategy.WARP: setValues([ [0, 0], [Math.round(placeholder.width / 3), 0], [Math.round(placeholder.width * (2 / 3)), 0], [placeholder.width, 0], ]); break; } setMode(_mode); }} /> ); } if (key.startsWith('x')) { const no = Number(key[1]); return ( values && values[no] && ); } if (key.startsWith('y')) { const no = Number(key[1]); return ( values && values[no] && ); } }}
); }; export default Customization;