Skip to content

Custom

自定义图形。

示例

使用路径自定义(推荐)

需要注意代码中 @registerUI()、strokeAlign、updateBoxBounds() 可选项。

ts
import { Leafer, UI, PathCommandDataHelper, affectStrokeBoundsType, PathBounds, Debug, registerUI, dataProcessor, UIData } from 'leafer-ui'
import { IStrokeAlign, IUIInputData, IUIData } from '@leafer-ui/interface'


// 定义数据

interface ICustomInputData extends IUIInputData { }
interface ICustomData extends IUIData { }

class CustomData extends UIData implements ICustomData {

}


// 定义类

const { moveTo, lineTo, closePath } = PathCommandDataHelper

@registerUI()
class CustomLine extends UI {

    public get __tag() { return 'CustomLine' }

    @dataProcessor(CustomData)
    declare public __: ICustomData

    // 画非闭合的线条,需要修改 strokeAlign 默认值为 ‘center’(UI默认是内描边)
    @affectStrokeBoundsType('center')
    declare public strokeAlign: IStrokeAlign

    constructor(data: ICustomInputData) {
        super(data)
        // ...
    }

    // 1. 绘制自定义路径
    public __updatePath() {
        const { width, height } = this.__
        const path: number[] = this.__.path = [] // 相当于 beginPath
        moveTo(path, 0, 0)
        lineTo(path, width, height)
        closePath(path)
    }

    // 2. (可选)如果通过width、height属性无法确定图形 bounds,需要override此函数,从路径中获取 bounds
    public __updateBoxBounds(): void {
        PathBounds.toBounds(this.__.path, this.__layout.boxBounds)
    }

}


const leafer = new Leafer({ view: window })
const custom = new CustomLine({ x: 100, y: 100, width: 200, height: 200, stroke: 'black', draggable: true })
leafer.add(custom)

Debug.enable = true
Debug.showRepaint = true // 通过显示重绘区域,查看图形边界是否准确
ts
import { Leafer, UI, PathCommandDataHelper, PathBounds, Debug, registerUI, dataProcessor, UIData } from 'leafer-ui'
import { IUIInputData, IUIData } from '@leafer-ui/interface'


// 定义数据

interface ICustomInputData extends IUIInputData { }
interface ICustomData extends IUIData { }

class CustomData extends UIData implements ICustomData {

}


// 定义类

const { moveTo, lineTo, closePath } = PathCommandDataHelper

@registerUI()
class CustomShape extends UI {

    public get __tag() { return 'CustomShape' }

    @dataProcessor(CustomData)
    declare public __: ICustomData

    constructor(data: ICustomInputData) {
        super(data)
        // ...
    }

    // 1. 绘制自定义路径
    public __updatePath() {
        const { width, height } = this.__
        const path: number[] = this.__.path = [] // 相当于 beginPath
        moveTo(path, 0, 0)
        lineTo(path, width, height / 2)
        lineTo(path, 0, height)
        closePath(path)
    }

    // 2. (可选)如果通过width、height属性无法确定图形 bounds,需要override此函数,从路径中获取 bounds
    public __updateBoxBounds(): void {
        PathBounds.toBounds(this.__.path, this.__layout.boxBounds)
    }

}


const leafer = new Leafer({ view: window })
const custom = new CustomShape({ x: 100, y: 100, width: 200, height: 200, fill: 'blue', draggable: true })
leafer.add(custom)

Debug.enable = true
Debug.showRepaint = true // 通过显示重绘区域,查看图形边界是否准确

使用 canvas.context 自定义

需要自己处理边界模型、样式、碰撞检测,一般用来对接其他的 canvas 库。

ts
import { Leafer, UI, registerUI, dataProcessor, UIData } from 'leafer-ui'
import { IUIInputData, ILeaferCanvas, IRadiusPointData, IUIData } from '@leafer-ui/interface'


// 定义数据

interface ICustomInputData extends IUIInputData { }
interface ICustomData extends IUIData { }

class CustomData extends UIData implements ICustomData {

}


// 定义类

@registerUI()
class Custom extends UI {

    public get __tag() { return 'Custom' }

    @dataProcessor(CustomData)
    declare public __: ICustomData

    constructor(data: ICustomInputData) {
        super(data)
        // ...
    }

    // 1. 如果通过width、height属性无法确定图形 bounds,需要重写此函数手动计算bounds
    __updateBoxBounds(): void {
        const box = this.__layout.boxBounds
        const { width, height } = this.__
        box.x = 0
        box.y = 0
        box.width = width
        box.height = height
    }

    // 2. 绘制碰撞路径
    __drawHitPath(hitCanvas: ILeaferCanvas): void {
        const { context } = hitCanvas
        const { x, y, width, height } = this.__layout.boxBounds
        context.beginPath()
        context.rect(x, y, width, height)
    }

    // 3. 碰撞检测(可选), 不重写此方法时,需要元素有fill或stroke值。
    __hit(inner: IRadiusPointData): boolean {
        const { context } = this.__hitCanvas
        if (context.isPointInPath(inner.x, inner.y)) return true

        // 碰撞半径
        const lineWidth = inner.radiusX * 2 // 可增加自定的线宽
        if (context.lineWidth !== lineWidth) {
            context.lineWidth = lineWidth
            context.stroke()
        }

        return context.isPointInStroke(inner.x, inner.y)
    }

    // 4. 绘制自定义内容
    __draw(canvas: ILeaferCanvas): void {
        const { context } = canvas
        const { width, height } = this.__

        canvas.setStrokeOptions(this.__)  // 绘制描边前,需要设置一下描边选项(可选)。

        context.fillStyle = 'blue'
        context.fillRect(0, 0, width / 2, height)

        context.strokeStyle = 'blue'
        context.strokeRect(width / 2, 0.5, width / 2, height - 1)
    }

}

const leafer = new Leafer({ view: window })

const custom = new Custom({ x: 100, y: 100, width: 200, height: 50, draggable: true })

leafer.add(custom)

Released under the MIT License.