packages/storybook/stories/render-flex.stories.tsx (150 lines of code) (raw):

import { createElement, DebugFlags, Point, Shader, Size } from '@canvas-ui/core' import type { StoryObj } from '@storybook/react' import React, { useEffect, useRef } from 'react' export const RenderFlexTest: StoryObj<React.FC> = () => { const canvasElRef = useRef<HTMLCanvasElement | null>(null) const prevFrameButtonRef = useRef<HTMLButtonElement | null>(null) const nextFrameButtonRef = useRef<HTMLButtonElement | null>(null) useEffect(() => { DebugFlags.set( DebugFlags.NodeBounds | DebugFlags.LayerBounds | DebugFlags.RasterCacheWaterMark ) return () => { DebugFlags.set(0) } }, []) 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 // // 构造如下结构 // a (View) // / \ // b c // / \ \ // d e f // / \ // g h (动态添加) const a = createElement('View') a.id = 'a' a.size = Size.fromWH(400, 400) // size 不对子节点造成约束 a.style.borderRadius = 32 a.style.borderImage = Shader.fromLinearGradient( Point.fromSize(a.size), Point.fromXY(0, 0), [{ offset: 0, color: '#83a4d4', }, { offset: 1, color: '#b6fbff', }] ) a.style.borderWidth = 4 a.style.boxShadow = '12px 12px 2px rgba(0, 0, 255, .2)' a.style.backgroundImage = Shader.fromLinearGradient( Point.fromXY(0, 0), Point.fromSize(a.size), [{ offset: 0, color: '#83a4d4', }, { offset: 1, color: '#b6fbff', }] ) const b = createElement('Flex') b.id = 'b' b.style.width = 490 // 允许超出父节点 b.style.height = 200 a.appendChild(b) const c = createElement('Flex') c.id = 'c' c.style.width = 450 // 允许超出父节点 c.style.height = 200 c.offset = Point.fromXY(0, 200) a.appendChild(c) const d = createElement('Flex') d.id = 'd' d.style.width = 50 d.style.height = 50 const e = createElement('Flex') e.id = 'e' e.style.width = 50 e.style.height = 100 const g = createElement('View') g.id = 'g' g.style.width = 25 g.style.height = 25 g.style.position = 'absolute' g.style.left = 33 g.style.top = 33 const h = createElement('View') h.id = 'h' h.style.width = 25 h.style.height = 50 h.style.position = 'absolute' h.style.right = 10 h.style.top = 10 type FrameCallback = () => void const frameCallbacks: readonly FrameCallback[] = [ // 0: initial frame () => { a.offset = Point.zero canvas.child = a }, () => { a.offset = Point.fromXY(50, 50) }, () => { b.appendChild(d) b.appendChild(e) }, () => { d.appendChild(g) d.appendChild(h) }, () => { b.removeChild(d) b.removeChild(e) }, () => { canvas.child = undefined } ] let frame = -1 const play = (direction = 1) => { frame += direction if (frame >= frameCallbacks.length) { frame = 0 } else if (frame < 0) { frame = frameCallbacks.length - 1 } console.info('frame', frame) 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> </> ) } RenderFlexTest.storyName = 'RenderFlex' export default { title: 'core/rendering', component: RenderFlexTest, decorators: [(Story: React.ComponentType) => <div style={{ backgroundColor: '#efefef', width: '100%', height: '100vh' }}><Story /></div>], }