您当前的位置: 首页 > 慢生活 > 程序人生 网站首页程序人生
uniapp利用canvas绘制阿姆斯勒量表测试(用户点选线条扭曲测试)
发布时间:2023-10-25 22:45:48编辑:雪饮阅读()
-
阿姆斯勒量表测试其中一项是对被测试用户点选的线条测试扭曲程度的。
那么要求就是用户可以在例如一个10x10画板上点选出线条。
然后根据自己的点选线条,选择眼中所见到的扭曲程度。
这里一般都是有这样的一个需求,就是在画板10x10格子的中心位置,也就是50,50坐标出需要一个可以闪烁的中心点。
那么这里就出现了以下几个任务:
(1)10x10的表格
(2)可以闪烁的中心点
(3)用户点选线条(实际就是用户点选的格子)
那么对于以上的任务主要是可以参考
https://uniapp.dcloud.net.cn/api/canvas/CanvasContext.html#
这里有相关api。
如果单独完成上面某个任务,基本都是很快就能搞定。
如果能在一个页面上同时实现,那么我这里总结主要就是两点。
(1)第一个关键就是理解canvas的绘制核心原理。
主要就是以路径为基础工作。
CanvasContext.beginPath
开始创建一个路径,需要调用fill或者stroke才会使用路径进行填充或描边。
那么有begin,必然有end,我的理解就是fill或者storke以及类似这两个函数的作用的都是可以有类似end的效果。
如果你只是绘制单个图形,那么自然不必理会,如果是多个图形。
那么每个图形都可以理解为一个路径的开合。
路径是一个比较抽象的概念,未必只是一条线路。
(2)第二个关键点就是,有时候你可能会想要使用类似ps的图册功能。
但是canvas还没有那么强大。虽然有类似的实现,但是比较菜比较鸡肋的感觉。
那么这里我的个人经验是比较推荐利用从不同canvas中获取上下文,而canvas视图本身利用z-index来控制。
那么为什么要用到上面两个关键点,首先就是这次的任务包含多个路径(图形),并且canvas并没有对于某个路径单独重绘(至少目前我还没有发现),所以对于闪烁点,我实际上的实现是将10x10以及中心点一起绘制,只不过每次判断下中心点半径不能和上次相同。
再结合时间比较短的间隔,实现了类似红点闪烁的效果。由于10x10的网格重绘都是一样的
并且重绘耗时也比较小,所以基本可以看不到整个视图的闪烁。(当然如果能支持某个路径重绘,那就更好了),那么如果整个canvas重绘,势必会导致用户点选的线条也重绘。这样一来整个视图闪烁的挺厉害(我没有过多测试,大概是这样的),那么最好的方案自然是另外新开一个canvas的context进行绘制,并且置于上个context之上,利用透明的原理实现。
那么多的不说,直接看看最终的效果
然后看看具体的实现test.vue:
<template>
<view style="width: 100vw; height: 70.286vw; position: relative">
<view style="width: 100vw; height: 70.286vw; left: 0vw; top: 0vw; position: absolute">
<view style="width: 100vw; height: 70.286vw; left: 0vw; top: 0vw; position: absolute; background: linear-gradient(180deg, #8ECCE5 0%, #1E9ED1 100%)"></view>
<view style="width: 99.964vw; height: 70.286vw; left: 0vw; top: 0vw; position: absolute; transform-origin: 0 0;background: url(/static/pad/common/2799x1968.png);background-size: 100% 100%;" ></view>
<view style="left: 43.5vw; top: 4.143vw; position: absolute; color: #095272; font-size: 1.714vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">阿姆斯勒量表測試</view>
<view style="width: 35.714vw; height: 11.429vw; left: 57.357vw; top: 15.75vw; position: absolute; background: white; border-radius: 3.571vw; border: 0.357vw #FE623F solid"></view>
<view style="width: 35.714vw; height: 11.429vw; left: 57.357vw; top: 29.679vw; position: absolute; background: white; border-radius: 3.571vw; border: 0.357vw #EEEEEE solid"></view>
<view style="width: 35.714vw; height: 11.429vw; left: 57.357vw; top: 43.607vw; position: absolute; background: white; border-radius: 3.571vw; border: 0.357vw #EEEEEE solid"></view>
<view style="width: 38.214vw; height: 5.357vw; left: 30.893vw; top: 58.429vw; position: absolute">
<view style="width: 17.036vw; height: 5.357vw; left: 21.179vw; top: 0vw; position: absolute; background: radial-gradient(62.34% 8.91% at 171.38% 4.19%, #FF7E5F 0%, #FE623F 100%); box-shadow: 0vw 1.714vw 2.143vw rgba(55.51, 15.11, 5.40, 0.20); border-radius: 3.571vw"></view>
<view style="left: 27.286vw; top: 1.25vw; position: absolute; color: white; font-size: 2.429vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">繼續</view>
<view style="width: 17.036vw; height: 5.357vw; left: 0vw; top: 0vw; position: absolute">
<view style="width: 17.036vw; height: 5.357vw; left: 0vw; top: 0vw; position: absolute; background: #D9D9D9; box-shadow: 0vw 1.714vw 2.143vw rgba(55.51, 15.11, 5.40, 0.20); border-radius: 3.571vw"></view>
<view style="left: 6.107vw; top: 1.25vw; position: absolute; color: #424242; font-size: 2.429vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">重設</view>
</view>
</view>
<!---->
<view style="width: 2.5vw; height: 2.5vw; left: 93.607vw; top: 3.929vw; position: absolute; background: #095272;background: url(/static/pad/amusilebiaoceshiniuquchengdu/Vector.png);background-size: 100% 100%;"></view>
<!--上一步-->
<view style="width: 3.571vw; height: 3.571vw; left: 3.214vw; top: 2.821vw; position: absolute;background: url(/static/pad/amusilebiaoceshiniuquchengdu/shangyibu.png);background-size: 100% 100%;">
</view>
<!--静音-->
<view style="width: 2.5vw; height: 2.5vw; left: 88.036vw; top: 3.929vw; position: absolute;background: url(/static/pad/amusilebiaoceshiniuquchengdu/jingyin.png);background-size: 100% 100%;">
</view>
</view>
<view style="left: 7.821vw; top: 9.571vw; position: absolute; color: #095272; font-size: 2.429vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">請你先在方格內點選出線條扭曲的位置</view>
<view style="left: 66.714vw; top: 9.571vw; position: absolute; color: #095272; font-size: 2.429vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">揀選扭曲的程度</view>
<view style="left: 68.714vw; top: 19.286vw; position: absolute; color: #FE623F; font-size: 4.571vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">A</view>
<view style="left: 68.714vw; top: 32.714vw; position: absolute; color: #095272; font-size: 4.571vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">B</view>
<view style="left: 68.714vw; top: 46.643vw; position: absolute; color: #095272; font-size: 4.571vw; font-family: Azeret Mono; font-weight: 700; word-wrap: break-word">C</view>
<view style="width: 41.286vw; height: 41.286vw; left: 7.821vw; top: 13.821vw; position: absolute;">
<canvas style="width: 100%; height: 100%;" id="grid" canvas-id="grid"></canvas>
</view>
<view style="width: 41.286vw; height: 41.286vw; left: 7.821vw; top: 13.821vw; position: absolute;z-index: 2;">
<canvas style="width: 100%; height: 100%;" id="userSelectArea" canvas-id="userSelectArea" @touchstart="canvasClick"></canvas>
</view>
<view style="width: 11.582vw; height: 38.286vw; left: 73.429vw; top: 16.25vw; position: absolute">
<view style="width: 11.536vw; height: 10.429vw; left: 0vw; top: 0vw; position: absolute;background: url(/static/pad/amusilebiaoceshiniuquchengdu/11.png);background-size: 100% 100%;" ></view>
<view style="width: 11.522vw; height: 10.429vw; left: 0.036vw; top: 13.679vw; position: absolute;background: url(/static/pad/amusilebiaoceshiniuquchengdu/21.png);background-size: 100% 100%;" ></view>
<view style="width: 11.546vw; height: 10.429vw; left: 0.036vw; top: 27.857vw; position: absolute;background: url(/static/pad/amusilebiaoceshiniuquchengdu/31.png);background-size: 100% 100%;" />
</view>
</view>
</template>
<script>
export default {
data() {
return {
radius:[10,5],
lastRadius:10,
timer:null,
canvasWidth:null,
canvasHeight:null,
canvas_cell_width:null,
canvas_cell_height:null,
canvas_context:null,
user_select_area:[],
canvas_context2:null
}
},
onReady: function (e) {
var that=this;
const query = uni.createSelectorQuery();
//获取宽度
query.select('#grid').fields({
size: true
},function(res){
that.canvasWidth=res.width;
that.canvasHeight=res.height;
that.canvas_context = uni.createCanvasContext('grid');
//画布2
that.canvas_context2=uni.createCanvasContext('userSelectArea');
//绘制网格,指定默认红点半径
that.drawGrid(that.lastRadius);
//绘制红点
that.drawFlickerPoint();
}).exec();
},
methods: {
drawFlickerPoint(){
var that=this;
that.canvas_context.clearRect(0, 0,that.canvasWidth,that.canvasHeight);
//that.DrawUserSelectArea(true);
that.drawGrid(null);
that.timer=setTimeout(function(){
that.drawFlickerPoint(null);
},1500);
},
drawGrid(FlickerPointRadius){
var that=this;
that.canvas_context.beginPath();
//设置背景颜色
that.canvas_context.fillStyle = 'white';
that.canvas_context.fillRect(0, 0, that.canvasWidth,that.canvasHeight);
// context.draw();
//设置线条宽度
that.canvas_context.setLineWidth(1);
// 设置线条颜色为黑色
that.canvas_context.setStrokeStyle("#000000");
//计算每条竖线之间的距离
that.canvas_cell_width=that.canvasWidth/10;
that.canvas_cell_height=that.canvasHeight/10;
//画出10条竖线
for(var i=0;i<=10;i++){
//不绘制最左边边框
if(i*that.canvas_cell_width==0){
continue;
}
//不绘制最右边边框
if(i*that.canvas_cell_width==that.canvasWidth){
continue;
}
that.canvas_context.moveTo(i*that.canvas_cell_width, 0);
that.canvas_context.lineTo(i*that.canvas_cell_width, that.canvasHeight);
}
//画出10条横线
for(var i=0;i<=10;i++){
//不绘制最顶上边框
if(i*that.canvas_cell_height==0){
continue;
}
//不绘制最底下的边框
if(i*that.canvas_cell_height==that.canvasHeight){
continue;
}
that.canvas_context.moveTo(0,i*that.canvas_cell_height);
that.canvas_context.lineTo(that.canvasWidth,i*that.canvas_cell_width);
}
that.canvas_context.stroke();
//保存绘制的10x10表格上下文
// context.save();
//绘制一个中心点的上下文
// 开始绘制路径
that.canvas_context.beginPath();
// 绘制一个圆形路径,半径为10,位置为(x, y)
var center_x = that.canvasWidth/2;
var center_y = that.canvasHeight/2;
var radius=this.lastRadius;
//没有指定默认红点半径,直接用最后一次红点半径进行取反
if(!FlickerPointRadius){
for(var i in this.radius){
if(this.radius[i]!=this.lastRadius){
radius=this.radius[i];
}
}
}
this.lastRadius=radius;
console.log("本次绘制半径",radius);
//参数1:圆心的x
//参数2:圆心的y坐标
//参数3:半径
//参数4:起始弧度
//参数5:终止弧度
that.canvas_context.arc(center_x,center_y,radius, 0, Math.PI * 2);
// 设置填充颜色为红色
that.canvas_context.fillStyle = 'red';
// 完成路径绘制,将圆形填充为红色
that.canvas_context.fill();
//所有路径绘制
that.canvas_context.draw();
},
canvasClick(event){
var that=this;
console.log("点击了canvas,event:",event);
var coordinate_x=event.touches[0].x;
var coordinate_y=event.touches[0].y;
//获取点击坐标所属区域x起点坐标
var select_area_x=null;
for(var i=0;i<=10;i++){
var i_x=i*that.canvas_cell_width;
var i_next_x=(i+1)*that.canvas_cell_width;
if(coordinate_x>=i_x && coordinate_x<i_next_x){
select_area_x=i_x;
}
}
var select_area_y=null;
for(var i=0;i<=10;i++){
var i_y=i*that.canvas_cell_height;
var i_next_y=(i+1)*that.canvas_cell_height;
if(coordinate_y>=i_y && coordinate_y<i_next_y){
select_area_y=i_y;
}
}
console.log("所选区域坐标:x:"+select_area_x+",y:"+select_area_y);
that.user_select_area.push({x:select_area_x,y:select_area_y});
this.DrawUserSelectArea();
//this.DrawUserSelectArea();
//that.canvas_context.draw();
/* that.canvas_context.save();
that.canvas_context.beginPath();
that.canvas_context.fillStyle='#D9D9D9'; */
/* var coordinate_x=center_x-2*cell_width+1;
var coordinate_y=center_y-cell_height+1; */
},
DrawUserSelectArea(){
var that=this;
/* that.canvas_context.clearRect(0, 0,that.canvasWidth,that.canvasHeight); */
var grayCellWidth=that.canvas_cell_width-2;
var grayCellHeight=that.canvas_cell_height-2;
console.log("that.user_select_area",that.user_select_area);
for(var i in that.user_select_area){
that.canvas_context2.beginPath();
that.canvas_context2.fillStyle='#D9D9D9';
var select_area_x=that.user_select_area[i].x;
var select_area_y=that.user_select_area[i].y;
that.canvas_context2.fillRect(select_area_x,select_area_y,grayCellWidth,grayCellHeight);
/* that.canvas_context2.strokeStyle = 'black'; */
that.canvas_context2.stroke();
}
that.canvas_context2.draw();
//that.canvas_context.draw();
}
}
}
</script>
<style>
</style>
关键字词:uniapp,canvas,阿姆斯勒量表