packages/storybook/stories/render-text.stories.tsx (146 lines of code) (raw):

import { createElement, Size } from '@canvas-ui/core' import type { StoryObj } from '@storybook/react' import React, { useEffect, useRef } from 'react' const LONG_WORD = `Honorificabilitudinitatibus califragilisticexpialidocious Taumatawhakatangihangakoauauotamateaturipukakapikimaungahoronukupokaiwhenuakitanatahu 次の単語グレートブリテンおよび北アイルランド連合王国で本当に大きな言葉。` const SHORT_WORD = `任务 0-0`.repeat(11) export const RenderTextTest: StoryObj<React.FC> = () => { const canvasElRef = useRef<HTMLCanvasElement | null>(null) const prevFrameButtonRef = useRef<HTMLButtonElement | null>(null) const nextFrameButtonRef = useRef<HTMLButtonElement | null>(null) useEffect(() => { // 等待布局稳定 setTimeout(() => { if (!canvasElRef.current) { return } const canvasRect = canvasElRef.current.getBoundingClientRect() const surfaceSize = Size.fromWH(canvasRect.width, canvasRect.height) const canvas = createElement('Canvas') canvas.prepareInitialFrame() canvas.el = canvasElRef.current canvas.size = surfaceSize canvas.dpr = devicePixelRatio // // 构造如下结构 // root // \ // a (Flex) // / \ \ // b c d // const root = createElement('View') canvas.child = root const a = createElement('Flex') root.appendChild(a) const b = createElement('Text') b.style.marginTop = 10 b.style.marginLeft = 10 b.style.maxWidth = 200 b.style.maxHeight = 200 b.text = `节点 id: b` b.repaintBoundary = true a.appendChild(b) const c = createElement('Text') c.style.width = 100 c.style.height = 200 c.style.borderColor = '#B5BAD0' c.style.borderRadius = 4 c.style.borderWidth = 1 c.style.backgroundColor = '#FFFFFF' c.style.boxShadow = '12px 12px 2px rgba(0, 0, 255, .2)' c.text = `节点 id: c` a.appendChild(c) const d = createElement('Text') d.style.marginTop = 10 d.style.width = 192 d.style.height = 300 d.style.maxLines = 1 d.style.fontSize = 14 d.text = SHORT_WORD d.repaintBoundary = true a.appendChild(d) const e = createElement('Text') e.style.borderColor = '#B5BAD0' e.style.borderRadius = 4 e.style.borderWidth = 1 e.style.backgroundColor = '#FFFFFF' e.style.boxShadow = '12px 12px 2px rgba(0, 0, 255, .2)' e.style.paddingLeft = 8 e.style.paddingRight = 8 e.style.paddingTop = 8 e.style.paddingBottom = 8 e.style.lineHeight = 16 e.style.fontSize = 16 e.style.maxWidth = 70 e.style.maxLines = 1 e.style.left = 100 e.style.top = 100 e.text = `节点 id: e` root.appendChild(e) const f = createElement('Text') f.style.borderColor = '#B5BAD0' f.style.borderRadius = 4 f.style.borderWidth = 1 f.style.backgroundColor = '#FFFFFF' f.style.boxShadow = '12px 12px 2px rgba(0, 0, 255, .2)' f.style.paddingLeft = 8 f.style.paddingRight = 8 f.style.paddingTop = 8 f.style.paddingBottom = 8 f.style.lineHeight = 16 f.style.fontSize = 16 f.style.width = 200 f.style.maxLines = 1 f.style.textAlign = 'center' f.text = `节点 f: 单行居中` root.appendChild(f) type FrameCallback = () => void const frameCallbacks: readonly FrameCallback[] = [ // 0: initial frame () => { b.style.color = '#ff0000' b.style.maxWidth = 180 b.text = `啤酒 🍺 b.style.maxHeight = 180\n${LONG_WORD}` }, () => { b.style.color = '#000' b.style.maxWidth = 200 b.text = `b.style.maxHeight = 200\n${LONG_WORD}` }, () => { d.style.lineHeight = 16 }, () => { d.style.lineHeight = 20 }, () => { d.style.marginTop = 30 }, () => { d.style.marginTop = 0 }, ] let frame = -1 const play = (direction = 1) => { frame += direction if (frame >= frameCallbacks.length) { frame = 0 } else if (frame < 0) { frame = frameCallbacks.length - 1 } frameCallbacks[frame]() } // 绘制首帧 play() prevFrameButtonRef.current?.addEventListener('click', () => { play(-1) }) nextFrameButtonRef.current?.addEventListener('click', () => { play(1) }) }, 100) }, [canvasElRef]) return ( <> <div> <button ref={prevFrameButtonRef}>prev frame</button> <button ref={nextFrameButtonRef}>next frame</button> </div> <canvas style={{ backgroundColor: '#ffffff', width: '500px', height: '500px' }} ref={canvasElRef}></canvas> </> ) } RenderTextTest.storyName = 'RenderText' export default { title: 'core/rendering', component: RenderTextTest, decorators: [(Story: React.ComponentType) => <div style={{ backgroundColor: '#efefef', width: '100%', height: '100vh' }}><Story /></div>], }