ICanvas - 旋转开关
演示
在线预览
在线模拟尺寸:
介绍
这一个旋转开关,主要演示如何控制手势旋转控制不同元素。以及如何绘制不同的元素方法,及局部更新。
示例源码
uvue
vue
<template>
<!-- #ifdef APP -->
<scroll-view style="flex:1">
<!-- #endif -->
<!-- #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']">
<canvas android-layer-type="hardware" :id="canvasId" @click="bindEvents" @touchstart="bindEvents" @touchmove="bindEvents"
@touchend="bindEvents" @touchcancel="bindEvents" @mousedown="bindEvents" @mousemove="bindEvents"
@mouseup="bindEvents"
<!-- #ifdef WEB -->
@mouseleave="bindEvents"
<!-- #endif -->
style="width: 100%;height: 450px;" ref="canvasDom"
>
</canvas>
</x-sheet>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup>
import { ICanvas } from '@/uni_modules/tmx-ui/core/canvas/ICanvas.uts';
import { CanvasEventType, ICanvasEvent } from '@/uni_modules/tmx-ui/core/canvas/interface.uts';
import { Shape } from '@/uni_modules/tmx-ui/core/canvas/lib/shape.uts';
import { IRect } from '@/uni_modules/tmx-ui/core/canvas/lib/rect.uts';
import { IText } from '@/uni_modules/tmx-ui/core/canvas/lib/text.uts';
import { Tween } from '@/uni_modules/tmx-ui/core/canvas/lib/animation/tween.uts';
import { ImageShape } from '@/uni_modules/tmx-ui/core/canvas/lib/image.uts';
import { Path2DShape } from '@/uni_modules/tmx-ui/core/canvas/lib/path2d.uts';
import { IRing } from '@/uni_modules/tmx-ui/core/canvas/lib/ring.uts';
import { IArc } from '@/uni_modules/tmx-ui/core/canvas/lib/arc.uts';
import { ISector } from '@/uni_modules/tmx-ui/core/canvas/lib/sector.uts';
import { IRegularPolygon } from '@/uni_modules/tmx-ui/core/canvas/lib/regularPolygon.uts';
import { ILinePolygon } from '@/uni_modules/tmx-ui/core/canvas/lib/linePolygon.uts';
import { ICircle } from '@/uni_modules/tmx-ui/core/canvas/lib/circle.uts';
import { IEllipse } from '@/uni_modules/tmx-ui/core/canvas/lib/ellipse.uts';
import { IStar } from '@/uni_modules/tmx-ui/core/canvas/lib/star.uts';
import { ILine } from '@/uni_modules/tmx-ui/core/canvas/lib/line.uts';
const canvasDom = ref<UniCanvasElement | null>(null)
const canvasId = "xCanvas-" + Math.random().toString(16).substring(4)
const proxy = getCurrentInstance()?.proxy ?? null;
const xcanvas = ref<ICanvas>(new ICanvas({ canvasId: canvasId, component: proxy as any | null }));
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 as Shape).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 oninit = () => {
xcanvas.value.init()
.then(() => {
drawer()
})
}
onReady(() => {
oninit()
})
onBeforeUnmount(()=>{
xcanvas.value?.destroy()
})
</script>
<style>
</style>