Skip to content

ICanvas - Library ​

警告

本画布文档由Cursor Ai根据我的uts文件自动编写的文档,如果有遇到描述错误的地方请通知我更新。

兼容 ​

HarmonyH5PCandroidIOS小程序UTSUNIAPP-X SDKversion
☑️☑️☑️☑️☑️☑️☑️4.75+1.1.16+

演示 ​

下面的演示中的元素可以拖动。

预览

介绍 ​

可以用于各种复杂的排版,动画,一些Lot实时绘制的动画场景,海报绘制,二给码等元素。 这是一个非常强大的绘画引擎.主要包含以下几个部分:

  1. 一个图形绘制系统
  2. 一个事件控制系统
  3. 一个动画控制系统

示例源码 ​

uvue
vue
<template>

		<!-- #ifdef MP-WEIXIN -->
		<page-meta :page-style="`background-color:${xThemeConfigBgColor}`">
			<navigation-bar :background-color="xThemeConfigNavBgColor"
				:front-color="xThemeConfigNavFontColor"></navigation-bar>
		</page-meta>
		<!-- #endif -->

		<x-sheet>
			<x-text font-size="18" class=" text-weight-b mb-8">绘画库 ICanvas</x-text>
			<x-text color="#999999" class="mb-8">有独立的事件及动画系统,下面绘制的元素都可点击、拖动。绘制走 x-skia-u,命中由 Skia Path2D 完成。</x-text>
		</x-sheet>
		<view class="xICanvasHost" :id="canvasId" ref="hostRef" :style="{width: canvasW + 'px', height: canvasH + 'px'}">
			<x-skia-u ref="skiaRef" :width="canvasW" :height="canvasH" @ready="onSkiaReady"></x-skia-u>
			<view class="xICanvasHit"
				@click="bindEvents"
				@touchstart="bindEvents"
				@touchmove.stop="bindEvents"
				@touchend="bindEvents"
				@touchcancel="bindEvents"
				@mousedown="bindEvents"
				@mousemove="bindEvents"
				@mouseup="bindEvents"
				<!-- #ifdef WEB -->
				@mouseleave="bindEvents"
				<!-- #endif -->
			></view>
		</view>
		<x-cell url="/pages/libs/canvasFlip" title="翻页效果"></x-cell>
		<x-cell url="/pages/libs/canvasRotarySwitch" title="旋转开关示例"></x-cell>
		<x-cell url="/pages/libs/canvasWatchSportOne" title="运动环示例"></x-cell>
		<x-cell url="/pages/libs/canvasVoiceLine" title="语音录制波"></x-cell>
		<x-cell url="/pages/libs/canvasPic" title="海报绘制演示"></x-cell>
		<x-cell url="/pages/libs/canvasLayout" title="自动排版"></x-cell>
		<x-cell url="/pages/libs/canvasGroup" title="组Group"></x-cell>
		<x-cell url="/pages/libs/canvasGravity" title="重力插件"></x-cell>
		
		<view style="height:150px"></view>

</template>

<script setup>
	import { ICanvas,CanvasEventType, ICanvasEvent ,ICanvasOptional ,
	IRect,IText,Tween,ImageShape,Path2DShape,IRing,IArc,ISector,IRegularPolygon,
	ILinePolygon,ICircle,IEllipse,IStar
	} from '@/uni_modules/tmx-ui';

	const canvasId = "xCanvas-" + Math.random().toString(16).substring(4)
	const canvasW = Math.max(1, Math.floor(uni.getWindowInfo().windowWidth))
	const canvasH = 350
	const skiaRef = ref(null)
	const hostRef = ref(null)
	const proxy = getCurrentInstance()?.proxy ?? null;
	const xcanvas = shallowRef<ICanvas>(new ICanvas({ canvasId: canvasId,component: proxy } as ICanvasOptional));
	// 添加受支持的事件。
	xcanvas.value.checkPointInHitEvents = ['down','move','up','click'] 
	let tw1 = null as Tween | null
	let tw2 = null as Tween | null
	let tw3 = null as Tween | null

	const initdrawer = () => {
		if (xcanvas.value == null) return;
		const render = xcanvas.value! as ICanvas
		const text = new IText({
			draggable: true,
			text:
				`这是一个非常强劲的图形渲染库\n有独立的事件及动画系统\n主要是用来制作营销动画交互及海报等场景\n有了它之后编排海报或者营销动画非常简单.`,
			fill: "#587cff",
			textAlign: 'right'
		}, render)
		const textBounds = text.getBoundRect();
		text.setY(render.boxHeight - textBounds.height - 44)
		text.setX(render.boxWidth - textBounds.width)
		text.addEventListener('click', (evt) => {
			text.setFill(text.toggleStatus ? '#f93e41' : '#587cff')
			render.update()
		})

		render.addShape(text)

		let sp = new ImageShape({
			draggable: true,
			src:'https://cdn.tmui.design/xui/tmui4.0banner1.jpg',
			width: 100,
			height: 56,
			x: render.boxWidth - 100,
			rotateCenter: "center",
			y: 20
		}, render)
		sp.addEventListener('click', (evt) => {
			sp.setScaleX(sp.toggleStatus ? 1.2 : 1)
			sp.setScaleY(sp.toggleStatus ? 1.2 : 1)
			render.update()
		})
		render.addShape(sp)
		let pathsape = new Path2DShape({
			draggable: true,
			fill: "#587cff", offsetX: 50, width: 100
		}, render)

		pathsape.parsePathData('M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0ZM66,21.25a8,8,0,1,1-8,8A8,8,0,0,1,66,21.25Zm-32,0a8,8,0,1,1-8,8A8,8,0,0,1,34,21.25Zm-14,38H80C75,85.25,25,85.25,20,59.25Z')
		pathsape.addEventListener('click', (evt) => {
			pathsape.setFill(pathsape.toggleStatus ? '#ff0000' : '#587cff')
			render.update()
		})

		render.addShape(pathsape)

		const rect = new IRect({
			draggable: true,
			x: 100, y: 100, width: 100, height: 50, fill: "#572cff", rotation: 45, opacity: 0.6,rotateCenter:'center'
		}, render)
		const rect2 = new IRect({
			draggable: true,
			x: 100, y: 100, width: 100, height: 50, fill: "#d348e5", radius: 8
		}, render)
		// rect2.drag = (event:CanvasEventType,parentEvent:ICanvasEvent)=>{
		// 	// let x = rect2.x-parentEvent.detail[0].moveLenX
		// 	// x = Math.max(0,Math.min(150,x))
		// 	// rect2.x = x
		// 	rect2.x-=parentEvent.detail[0].moveLenX
		// 	rect2.y-=parentEvent.detail[0].moveLenY
		// }
		rect2.addEventListener('click', (evt) => {
			rect2.setFill(rect2.toggleStatus ? '#ff6939' : '#81144f')
			render.update()
		})
		rect.addEventListener('click', (evt) => {
			rect.setFill(rect.toggleStatus ? '#ef3c9e' : '#ffac69')
			render.update()
		})
		render.addShape(rect2)
		render.addShape(rect)
		const ring = new IRing({
			draggable: true,
			fill: "#ff0000",
			x: 50,
			y: 110,
			endAngle: 90
		}, render)
		ring.addEventListener('click', (evt) => {
			ring.setFill(ring.toggleStatus ? '#ffac69' : '#ff0000')
			render.update()
		})

		render.addShape(ring)

		const arc = new IArc({
			draggable: true,
			stroke: "#ff0000",
			x: 40,
			y: 160,
			strokeWidth: 8,
			lineJoin: 'round',
			lineCap: "round",
			lineDashOffset: 5,
			endAngle: 135
		}, render)

		arc.addEventListener('click', (evt) => {
			let target = evt.target as IArc
			target.setStroke(target.stroke == '#ff0000' ? '#ffac69' : '#ff0000')
			render.update()
		})
		render.addShape(arc)

		for (let i = 0; i < 24; i++) {
			const colortest = `rgb(255,${10 * (i + 1)},${10 * (i + 1)})`
			const tarc = new ISector({
				draggable: true,
				fill: colortest,
				x: 240,
				y: 120,
				radius: i * 2 + 14,
				startAngle: 15 * i,
				endAngle: 15 * (i + 1)
			}, render)

			tarc.addEventListener('click', (evt) => {
				tarc.setFill(tarc.toggleStatus ? '#ffc61e' : colortest)
				render.update()
			})

			render.addShape(tarc)
		}

		const text2 = new IText({
			draggable: true,
			text: "点我保存画布图片!",
			fill: "#ffffff",
			textAlign: 'center',
			padding:16,
			fontSize: 16,
			lineHeight: 1,
			bubbleEvent: true,
			textBaseline: 'middle',
			radius:12,
			textBgColor:"rgb(5, 121, 255)"
		}, render)

		text2.toggle((status : boolean, target : any) => {
			if (status) {
				text2.setText("已导出预览图片")
				text2.setTextBgColor('#ff0000')
				render.getImage().then((imgpath:string)=>{
					console.log(imgpath)
					uni.previewImage({
						current:imgpath,
						urls:[imgpath]
					})
				})
			} else {
				text2.setText("点我保存画布图片")
				text2.setTextBgColor('rgb(5, 121, 255)')
			}
			
			render.update()
		})
	
		const text2Bounds = text2.getBoundRect();
		text2.setY(render.boxHeight - text2Bounds.height)
		render.addShape(text2)
		const polygon = new IRegularPolygon({
			draggable: true,
			sides: 5,
			x: 50,
			y: 250,
			fill: "#ff0000",
		}, render)
		polygon.addEventListener('click', (evt) => {
			polygon.setSides(polygon.toggleStatus ? 6 : 5)
			render.update()
		})
		render.addShape(polygon)

		const linepolygon = new ILinePolygon({
			draggable: true,
			points: [0, 0, 40, 60, 80, 0, 120, 60, 160, 0, 200, 60],
			x: 90,
			y: 220,
			strokeWidth: 8,
			stroke: "#ff0000",
			lineCap: 'round',
			opacity: 0.5,
			tension: 0.5,
			closed: false,
			bezier: true
		}, render)
		linepolygon.addEventListener('click', (evt) => {
			linepolygon.setStroke(linepolygon.toggleStatus ? "#ffff00" : "#ff0000")
			render.update()
		})
		render.addShape(linepolygon)


		const circle = new ICircle({
			draggable: true,
			x: 220,
			y: 200,
			strokeWidth: 8,
			stroke: "#18e7dd",
			lineCap: 'round',
			fill: "#5f5cf0"

		}, render)
		circle.addEventListener('click', (evt) => {
			circle.setStroke(circle.toggleStatus ? "#5f5cf0" : "#18e7dd")
			render.update()
		})
		render.addShape(circle)

		const ellipse = new IEllipse({
			draggable: true,
			x: 220,
			y: 200,
			strokeWidth: 6,
			stroke: "#f08200",
			lineCap: 'round',
			// lineDash:[2,0,0,8]
		}, render)

		ellipse.addEventListener('click', (evt) => {
			ellipse.setStroke(ellipse.toggleStatus ? "#f4483b" : "#f08200")
			// ellipse.setLineDash(ellipse.toggleStatus ? [2,0,0,0] : [2,0,0,8])
			render.update()
		})
		render.addShape(ellipse)

		const star = new IStar({
			draggable: true,
			x: 280,
			y: 240,
			strokeWidth: 6,
			numPoints: 6,
			stroke: "rgba(255,0,0,1)",
			fill: "rgba(255,0,0,0.5)"
		}, render)
		star.addEventListener('click', (evt) => {
			star.setStroke(star.toggleStatus ? "#f4483b" : "rgba(255,0,0,1)")
			star.setNumPoints(star.toggleStatus ? 5 : 6)

			render.update()
			
			
		})
		render.addShape(star)



		render.render()

		// tw1 = new Tween(rect, render)
		// 	.to({ radius: 24 })
		// 	.setYoyo(true)
		// 	.setLoop(-1)
		// 	.start()

		// tw2 = new Tween(polygon, render)
		// 	.to({ scaleX: 1.3, scaleY: 1.3 })
		// 	.setYoyo(true)
		// 	.setLoop(-1)
		// 	.start()

		// tw3 = new Tween(ring, render)
		// 	.addTo({ endAngle: 270 })
		// 	// .addTo({ x: 100 })
		// 	.setYoyo(true)
		// 	.setLoop(-1)
		// 	.onComplete(() => {
		// 		console.log('ok')
		// 	})
		// 	.start()

	}

	const bindEvents = (evt : UniTouchEvent | UniPointerEvent | UniMouseEvent) => {
		xcanvas.value.bindEvent(evt)
	
	}
	const onSkiaReady = () => {
		const ctx = skiaRef.value?.getContext("2d")
		if (ctx == null) return
		xcanvas.value.setHitElement(hostRef.value)
		xcanvas.value.onSurfaceReady(ctx, initdrawer)
	}
	onPageScroll(() => {
		xcanvas.value.syncLayout()
	})
	onBeforeUnmount(() => {
		tw1?.stop()
		tw2?.stop()
		tw3?.stop()
		xcanvas.value?.destroy()
	})
	
</script>

<style>
	.xICanvasHost {
		position: relative;
		width: 100%;
		overflow: hidden;
	}
	.xICanvasHit {
		position: absolute;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
	}
</style>
最近更新