ICanvas - 旋转开关
演示
预览
介绍
这一个旋转开关,主要演示如何控制手势旋转控制不同元素。以及如何绘制不同的元素方法,及局部更新。
示例源码
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">旋转开关示例</x-text>
<x-text color="#999999" class="mb-8">下面是一个绘制的旋转示例,请在指针圆圈内左右滑动来设置房间温度.</x-text>
</x-sheet>
<x-sheet color="#f0f0f0" dark-color="#f0f0f0" :padding="['0']">
<view class="xICanvasHost" :id="canvasId" :style="{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-sheet>
</template>
<script setup>
import { ICanvas,CanvasEventType, ICanvasEvent ,ICanvasOptional ,ILine,ILayout,
IRect,IText,Tween,ImageShape,Path2DShape,IRing,IArc,ISector,IRegularPolygon,IGroup,
ILinePolygon,ICircle,IEllipse,IStar,Easing,EasingFunction,GravityPlugin, GravityManager, addGravityToShape
} 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 = 450
const skiaRef = ref(null)
const proxy = getCurrentInstance()?.proxy ?? null;
const xcanvas = shallowRef<ICanvas>(new ICanvas({ canvasId: canvasId, component: proxy} as ICanvasOptional));
let tw1 = null as Tween | null
let tw2 = null as Tween | null
let tw3 = null as Tween | null
const bindEvents = (evt : UniTouchEvent | UniPointerEvent | UniMouseEvent) => {
xcanvas.value.bindEvent(evt)
}
let bgcolorRect = null as null|IRect;
let progereeBgColor = null as null|IArc;
const drawerBgColor = () =>{
const render = xcanvas.value! as ICanvas
let rect = new IRect({
x: 0,
y: 0,
width:render.boxWidth,
height:render.boxHeight,
fill:`rgb(32,32,38,1)`,
fillGradient:['45deg','#B5FFFF 0%','#fed7f1 100%'],
}, render)
render.addShape(rect)
bgcolorRect = rect
}
const drawerProgressCirle = ()=>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 150
let rouleCire = new IArc({
startAngle: 0,
endAngle: 0,
strokeWidth: 16,
stroke: "#FCA5F1",
strokeGradient:['180deg','#B5FFFF 0%','#fed7f1 50%','#fe4faa 100%'],
x: centerX,
y: centerY,
radius: radius,
rotateCenter:"center",
lineCap:'round',
rotation:135
}, render)
progereeBgColor = rouleCire
render.addShape(rouleCire)
}
// 绘制刻度
const drawerRule = () => {
if (xcanvas.value == null) return;
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 150
// 首先定义刻度的参数
const divisions = 45; // 等分数量
const tickLength = 15; // 刻度长度
const innerRadius = radius - tickLength; // 内径半径(从这里开始向外画刻度)
// 计算起始角度和结束角度(转换为数学角度)
const startAngle = 135;
const endAngle = 45;
// 处理角度范围(确保endAngle大于startAngle)
const totalAngle = endAngle <= startAngle ? endAngle + 360 - startAngle : endAngle - startAngle;
// 计算每个刻度之间的角度
const angleStep = totalAngle / divisions;
// 绘制刻度
for (let i = 0; i <= divisions; i++) {
// 当前刻度的角度
const currentAngle = (startAngle + i * angleStep) * (Math.PI / 180);
// 计算刻度起点(内径上的点)
const innerX = centerX + innerRadius * Math.cos(currentAngle);
const innerY = centerY + innerRadius * Math.sin(currentAngle);
// 计算刻度终点(外径上的点)
const outerX = centerX + radius * Math.cos(currentAngle);
const outerY = centerY + radius * Math.sin(currentAngle);
// 创建刻度线
const tick = new ILine({
pointStart: { x: innerX, y: innerY },
pointEnd: { x: outerX, y: outerY },
stroke: `#FCA5F1`,
strokeWidth: 1,
lineCap:'round'
}, render);
// 添加到画布
render.addShape(tick);
}
}
// 绘制外圆弧
const outLineArc = ()=>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 150
let rouleCire = new IArc({
startAngle: 135,
endAngle: 45,
strokeWidth: 2,
stroke: "#FCA5F1",
x: centerX,
y: centerY,
radius: radius,
lineCap:'round',
lineDash: []
}, render)
render.addShape(rouleCire)
}
// 绘制内圆
const innerArc = ()=>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 120
let rouleCire = new ICircle({
x: centerX,
y: centerY,
radius: radius,
fill:"#3884ff",
fillGradient:['45deg','#B5FFFF 0%','#FCA5F1 100%'],
}, render)
render.addShape(rouleCire)
}
// 绘制内拨线
const rotaeLine = ():ILine=>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 60
let line = new ILine({
stroke:'#B5FFFF',
lineCap:'round',
strokeWidth:6,
bubbleEvent:true,
pointStart:{x:centerX,y:centerY},
pointEnd:{x:centerX+radius,y:centerY},
},render)
render.addShape(line)
return line
}
const drawerLabel = ():IText =>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 50
let labeltext = new IText({
x: 0,
y: centerY+130,
width:render.boxWidth,
textAlign:"center",
text:"房间温度",
fontSize:20,
fill:'#FCA5F1'
}, render)
render.addShape(labeltext)
return labeltext
}
const angleToProgress = (angle:number):number=>{
let base = 1/8
let progeress = 0
if(angle>=135&&angle<=180){
progeress = base - (180-angle)/45 * base
}else if(angle>=-180&&angle<=0){
angle = Math.abs(angle)
progeress =(180-angle)/180 * base*6 + base
}else if(angle>0&&angle<=45){
progeress = base*7+(angle)/45*base
}else{
progeress=1
}
return progeress;
}
// 绘制内圆
const innerArc2 = ()=>{
const render = xcanvas.value! as ICanvas
let centerX = render.boxWidth / 2
let centerY = render.boxHeight / 2
let radius = 80
let rouleCire = new ICircle({
x: centerX,
draggable:true,
y: centerY,
radius: radius,
fill:"#fff",
stroke:"#B5FFFF",
strokeWidth:2,
fillGradient:['45deg','#FCA5F1 0%','#B5FFFF 100%'],
rotateCenter:'center'
}, render)
render.addShape(rouleCire)
let smallCirle = new ICircle({
x: centerX,
y: centerY,
radius: 10,
fill:"#B5FFFF",
stroke:"#FCA5F1",
strokeWidth:2,
bubbleEvent:true,
rotateCenter:'center'
}, render)
render.addShape(smallCirle)
const line = rotaeLine()
line.rotateByPoint(true,135)
let newAngle = line.getAngle()
rouleCire.addEventListener('down',(e)=>{
new Tween(rouleCire,render)
.addTo({scaleX:1.1,scaleY:1.1},200)
.start()
newAngle = line.getAngle()
})
render.addEventListener('up',(e)=>{
new Tween(rouleCire,render)
.addTo({scaleX:1,scaleY:1},200)
.start()
})
const text:IText = drawerLabel() as IText
rouleCire.drag = (eventName,parentEvent)=>{
let detail = parentEvent.detail[0];
const endy = detail.moveY - line.pointStart.y
let ro = line.getAngleByAnchor(
centerX,
centerY,
detail.startX,
detail.startY,
detail.moveX,
detail.moveY
)
let rueslt = newAngle+ro
if(rueslt>=0&&rueslt<=45){
rueslt = Math.min(45,Math.max(0,rueslt))
}else if(rueslt >=135&&rueslt <=180){
rueslt = Math.min(180,Math.max(135,rueslt))
}else if(rueslt < 0 && rueslt >=-180){
rueslt = Math.max(-180,Math.min(0,rueslt))
}else if(rueslt >45 && rueslt <135){
rueslt = line.getAngle()
}
line.rotateByPoint(true, rueslt);
let progress = angleToProgress(line.getAngle())
text.setText("房间温度:"+(progress*36).toFixed(0))
bgcolorRect?.setOpacity(1-progress)
progereeBgColor?.setEndAngle(Math.min(270,Math.max(270 * progress,0)))
render.update();
}
}
const drawer = () => {
if (xcanvas.value == null) return;
const render = xcanvas.value! as ICanvas
render.checkPointInHitEvents = ['down','move','up','cancel']
drawerBgColor()
drawerRule()
outLineArc()
drawerProgressCirle()
innerArc()
innerArc2()
render.render()
}
const onSkiaReady = () => {
const ctx = skiaRef.value?.getContext("2d")
if (ctx == null) return
xcanvas.value.onSurfaceReady(ctx, drawer)
}
onBeforeUnmount(()=>{
xcanvas.value?.destroy()
})
</script>
<style>
.xICanvasHost { position: relative; width: 100%; overflow: hidden; }
.xICanvasHit { position: absolute; left: 0; top: 0; right: 0; bottom: 0; }
</style>