Skip to content

ImagePaint Object

Pattern fill object, can be assigned to the fill or stroke property. Supports using images in svg format.

In repeat mode, 4k images exceeding 4096 x 2160 will be optimized for display.

Key Properties

type: string

The fill type is image.

The fill type also supports film and video, which require installing corresponding extension plugins.

url: string

Image URL, supports Blob URL and Data URL (Base64).

We also provide a Resource, which supports converting original image objects and canvas objects to URLs, and preloading images.

mode?: ImagePaintMode

Pattern fill mode, default is cover.

ts
type ImagePaintMode =
  | 'normal' // normal
  | 'cover' // cover (equivalent to background-size: cover)
  | 'fit' // fit (equivalent to background-size: contain)
  | 'stretch' // stretch, will change image ratio
  | 'clip' // clip
  | 'repeat' // repeat (equivalent to background-repeat: repeat)

format?: 'svg' | 'jpg' | 'png' | 'webp'

Supplementary image format, currently mainly used for svg images that cannot be recognized from the URL.

changeful?: boolean

Whether it changes frequently, default is false.

After setting to true, no additional high-performance pattern will be generated. Commonly used for frequently changing URLs such as game sprites or animated images.

sync?: boolean

Whether to synchronously update the image layer cache, default is false (for performance optimization).

When enabled, scaling the image will not produce blur, but will increase performance cost. Use it appropriately.

Basic Properties

blendMode?: BlendMode

Blend mode, default is normal.

visible?: boolean

Whether it is visible, default is true.

opacity?: number

Opacity, default is 1. Currently only effective for color objects and images.

align: IAlign

Background image alignment, similar to CSS background-position. Not supported in clip mode.

Direction Diagram

ts
// Alignment
type IAlign =
  | 'top-left'
  | 'top'
  | 'top-right'
  | 'right'
  | 'bottom-right'
  | 'bottom'
  | 'bottom-left'
  | 'left'
  | 'center'

cover Mode Properties

rotation?: number

Rotation angle, rotates in increments of 90 degrees.

fit Mode Properties

rotation?: number

Rotation angle, rotates in increments of 90 degrees.

padding?: IFourNumber

Padding, default is 0.

repeat?: boolean

Whether to repeat the background.

clip Mode Properties

clipSize?: ISizeData

Clip frame size, the clipped content will be automatically stretched to fit the element's width and height.

ts
interface ISizeData {
  width: number // width
  height: number // height
}

offset?: IPointData

Offset position.

size?: numberIOptionSizeData

Image size, automatically converted to scale.

ts
// Set width or height individually, the other side adapts to the original ratio; setting both will stretch
interface IOptionSizeData {
  width?: number // width, when only width is set, height adapts to the original ratio
  height?: number // height, when only height is set, width adapts to the original ratio
}

scale?: numberIPointData

Scale size, priority is given to the scale calculated from size.

rotation?: number

Rotation angle.

repeat Mode Properties

offset?: IPointData

Offset position.

size?: numberIOptionSizeData

Image size, automatically converted to scale.

ts
// Set width or height individually, the other side adapts to the original ratio; setting both will stretch
interface IOptionSizeData {
  width?: number // width, when only width is set, height adapts to the original ratio
  height?: number // height, when only height is set, width adapts to the original ratio
}

scale?: numberIPointData

Scale ratio of repeated images, priority is given to the scale calculated from size.

scaleFixed?: boolean | 'zoom-in'

Fix the global scale ratio of repeated images, not affected by canvas scaling. Default is false.

'zoom-in' means fixed only when zooming in (still follows when zooming out).

rotation?: number

Rotation angle, rotates in increments of 90 degrees.

After enabling freeTransform, any angle can be set.

freeTransform?: boolean

Whether to use free transform. This ignores the special rotation logic and is convenient for manual editing.

gap?: IGap | IPointGap

Spacing between repeated images, default is 0.

ts
// Setting auto / fit will evenly distribute the remaining space. auto minimum is 0, fit allows negative values.
type IGap = number | 'auto' | 'fit'

interface IPointGap {
  x?: IGap // set x-axis spacing individually
  y?: IGap // set y-axis spacing individually
}

interlace?: number | IPercentData | IInterlace

Interlaced arrangement of repeated images.

ts
interface IInterlace {
  type: 'x' | 'y'
  offset: number | IPercentData
}

interface IPercentData {
  type: 'percent'
  value: number
}

// number
interlace: 100 // next row offset 100px on X axis

// percentage
interlace: { type: 'percent', value: 0.5}  // next row offset 50% of pattern size on X axis

// Y-axis interlace
interlace: { // next column offset 50% of pattern size on Y axis
  type: 'y'
  offset:  { type: 'percent', value: 0.5}
}

repeat?: IRepeat

Repeat mode, can set repetition on x or y axis, default repeats both axes.

Also supports setting the number of tiles on x and y axes individually.

ts
type IRepeat = boolean | 'x' | 'y' | IPointData

const repeat = { x: 10, y: 6 } // set number of tiles on x and y axes

Sub-stroke Properties

style?: IStrokeStyle

When multiple strokes are set for an element, you can configure the sub-stroke style style to override the main stroke style.

This can create effects such as dashed lines, or simulate inner, middle, and outer triple strokes. Learn more.

Image Cache

Global configuration for image caching, can be adjusted dynamically as needed.

ts
import { Platform } from 'leafer-ui'

// 最大缓存等级,默认2k: 2560 * 1600 像素
Platform.image.maxCacheSize = 2560 * 1600

// 最大重复pattern缓存等级, 默认4k: 4096 * 2160 像素
Platform.image.maxPatternSize = 4096 * 2160

// 图片后缀,区分dom中image标签的缓存,否则可能会有浏览器缓存跨域问题,默认: leaf
Platform.image.suffix = 'leaf'  // image.jpg?leaf

Image Cross-Origin

Global configuration for image cross-origin, can be adjusted dynamically as needed.

When set to null, cross-origin images not allowed by the server can be rendered, but exporting the canvas content is not supported (browser limitation).

ts
import { Platform } from 'leafer-ui'

// 默认配置,未经服务端允许的跨域图片不能渲染。
Platform.image.crossOrigin = 'anonymous'

// 允许跨域图片渲染,但不支持导出画板内容(浏览器的限制)。
Platform.image.crossOrigin = null

Resource

We also provide a Resource, which can preload images. Original image objects and canvas objects can be converted to URLs.

All images in the engine are loaded in an orderly and parallel manner through the Resource. When images are no longer used, they enter a recycle list and will be automatically destroyed when the threshold is reached.

Image Events

ImageEvent

Belongs To

UI Element

Examples

cover Mode

ts
// #图案填充 [默认 cover 覆盖模式 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        // mode: 'cover' // 默认模式,相当于 CSS 的 background-size: cover
    }
})

leafer.add(rect)
ts
// #图案填充 [默认 cover 覆盖模式 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        // mode: 'cover' // 默认模式,相当于 CSS 的 background-size: cover
    }
})

app.tree.add(rect)

cover Mode rotated 90 degrees

ts
// #图案填充 [cover 覆盖模式旋转 90 度 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        rotation: 90
    }
})

leafer.add(rect)
ts
// #图案填充 [cover 覆盖模式旋转 90 度 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        rotation: 90
    }
})

app.tree.add(rect)

fit Mode

ts
// #图案填充 [fit 适应模式 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'fit' // 相当于 CSS 的 background-size: contain
    }
})

leafer.add(rect)
ts
// #图案填充 [fit 适应模式 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'fit' // 相当于 CSS 的 background-size: contain
    }
})

app.tree.add(rect)

stretch Mode

ts
// #图案填充 [stretch 拉伸模式 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 120,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'stretch'
    }
})

leafer.add(rect)
ts
// #图案填充 [stretch 拉伸模式 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 120,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'stretch'
    }
})

app.tree.add(rect)

clip Mode

ts
// #图案填充 [clip 裁剪模式 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'clip',
        offset: { x: -40, y: -90 },
        scale: { x: 1.1, y: 1.1 },
        rotation: 20
    }
})

leafer.add(rect)
ts
// #图案填充 [clip 裁剪模式 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'clip',
        offset: { x: -40, y: -90 },
        scale: { x: 1.1, y: 1.1 },
        rotation: 20
    }
})

app.tree.add(rect)

repeat Mode

ts
// #图案填充 [repeat 平铺模式 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'repeat', // 相当于 CSS 的 background-repeat: repeat
        scale: 0.2
    }
})

leafer.add(rect)
ts
// #图案填充 [repeat 平铺模式 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'repeat', // 相当于 CSS 的 background-repeat: repeat
        scale: 0.2
    }
})

app.tree.add(rect)

repeat Mode rotated 90 degrees

ts
// #图案填充 [repeat 平铺模式旋转 90 度 (Leafer)]
import { Leafer, Rect } from 'leafer-ui'

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

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'repeat',
        scale: 0.2,
        rotation: 90
    }
})

leafer.add(rect)
ts
// #图案填充 [repeat 平铺模式旋转 90 度 (App)]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({ view: window, editor: {} })

const rect = new Rect({
    width: 100,
    height: 100,
    fill: {
        type: 'image',
        url: '/image/leafer.jpg',
        mode: 'repeat',
        scale: 0.2,
        rotation: 90
    }
})

app.tree.add(rect)

repeat Mode does not scale with canvas

ts
// #图形编辑器 [背景为透明方格的画板]
import { App, Frame, Rect, Platform } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件
import '@leafer-in/viewport' // 导入视口插件 (可选)

const app = new App({
    view: window,
    fill: '#333',
    editor: {},  //  配置 editor 会自动创建并添加 app.editor 实例、tree 层、sky 层
})

// 平铺的透明方格
const svg = Platform.toURL(
    `<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="5" height="5" fill="#FFF"/><rect x="5" y="0" width="5" height="5" fill="#CCC"/>
<rect x="0" y="5" width="5" height="5" fill="#CCC"/><rect x="5" y="5" width="5" height="5" fill="#FFF"/>
</svg>`, 'svg',)


app.tree.add(Frame.one({ // 背景为透明方格的画板
    fill: {
        type: 'image',
        url: svg,
        mode: 'repeat',
        scaleFixed: 'zoom-in' // true // 固定平铺图比例,不随画布缩放
    },
    shadow: {
        x: 0,
        y: 3,
        blur: 15,
        color: '#0009',
        scaleFixed: 'zoom-in' // 固定阴影比例,不随画布放大
    },
    children: [
        Rect.one({ editable: true, fill: '#FEB027', cornerRadius: [20, 0, 0, 20] }, 100, 100),
        Rect.one({ editable: true, fill: '#FFE04B', cornerRadius: [0, 20, 20, 0] }, 300, 100)
    ]
}, 100, 100, 500, 600))

Released under the MIT License.