完成效果:
在web页面中,如果我们要实现左右滑动的效果,只需要在css中写上:
overflow: hidden;
overflow-x: scroll;
就可以达到效果。但是在快应用中,不支持overflow命令。
所以这里用canvas画图来实现:监听手指左右滑动的事件,手指每滑动一次,重新绘制一次尺子 从而给人视觉上滑动的效果。
核心代码:
template:
<div show="{{showRule}}" class="cover" @click="hideRule"></div>
<!-- 食物选择尺寸 -->
<div show="{{showRule}}" class="Choice_count">
<div class="date_title">
<div class="date">
<text class="dateText" >{{shortDate==today?'今天':shortDate.substr(0,4)+"年"+shortDate.substr(4,2)+"月"+shortDate.substr(6,2)+"日"}}</text>
</div>
<div class="images" @click="hideRule">
<image src="../Common/icon_close.png" ></image>
</div>
</div>
<div class="food_detail" @click="goFoodDetailPage">
<div class="food_info" >
<image class="foodpic" src="{{!foodInfo.ImageUrl?'https://static.168play.cn/health/images/default.png':foodInfo.ImageUrl}}"></image>
<div class="food_text">
<text class="fondname">{{foodInfo.FoodName}}</text>
<text class="num" style="margin-top:4px;">{{foodInfo.Calory}}千卡/{{foodInfo.Weight}}{{foodInfo.UnitName}}</text>
</div>
</div>
<div style="width:90px;height;160px;align-items: center;justify-content: center;" if="{{!foodInfo.IsCustom}}">
<image style="height:25px;" src="../Common/degree_return.png"></image>
</div>
<div style="width:90px;height;160px;align-items: center;justify-content: center;" else>
<image style="height:36px;" src="../Common/food_modify.png"></image>
</div>
</div>
<div class="rule_module">
<div class="amount_module">
<text class="amount" >{{(weight_initParams.count*(weight_initParams.minScale)).toFixed(1)}}</text>
<text>{{activeUnitInfo.UnitName}}</text>
</div>
<div>
<text class="num">约{{((activeUnitInfo.Calory/activeUnitInfo.Amount)*(weight_initParams.count*(weight_initParams.minScale))).toFixed(1)}}千卡 {{((activeUnitInfo.Weight/activeUnitInfo.Amount)*(weight_initParams.count*(weight_initParams.minScale))).toFixed(1)}}{{foodInfo.UnitName}}</text>
</div>
<div class="ruler">
<canvas class="new-canvas" id="weight-canvas" @touchmove="move('weight_initParams')" @touchstart="start('weight_initParams')" @touchend="end('weight_initParams')" ></canvas>
</div>
</div>
<div class="unitList">
<div for="{{foodUnitInfo}}" class="foodUnit" @click="changeUnit($idx)">
<text class=" {{$item.UnitName==activeUnitInfo.UnitName?'unitActive':'unit'}}" >{{$item.UnitName}}</text>
</div>
</div>
<input class="button" type="button" value="确定" onclick="addFoodFromRule" />
</div>
Css:
.cover{width:100%;height: 100%;background-color: #000000;position: fixed;top: 0px;left: 0px;opacity: 0.7}
.Choice_count{width: 100%;height:840px;position: fixed;bottom: 0;left: 0;background-color: #ffffff;flex-direction: column;border-top-left-radius:20px;border-top-right-radius:20px}
.date_title{width:100%;height:120px;border-bottom: 1px solid #EAE5DD;align-items: center;}
.dateText{font-size:36px;color: #473B1D;position:absolute;}
.date{width:100%;height: 120px;position: relative;align-items: center;justify-content: center;}
.foodUnit{width:140px;align-items: center;justify-content: center;}
.foodUnit text{lines:1;text-overflow:ellipsis;}
.button{background-color: #FFD359;width:100%;height: 105px;font-size: 32px;color: #473B1D;font-weight: bold;position:absolute;bottom: 0;left: 0;}
.unitList{align-items:center;justify-content: center;}
.unit{padding:10px,0;color:#473b1d;font-size: 32px;}
.unitActive{padding:10px,0;color:#ffd359;font-weight:bold;font-size: 32px;}
.amount_module text{color:#473b1d;font-weight:700}
.amount_module{width:100%;justify-content:center;align-items:flex-end;font-family:'Microsoft YaHei';}
.rule_module{width:100%;background-color:#fff;flex-direction:column;align-items: center;}
.amount{font-size:72px;font-weight:700;position:relative;top:12px;margin-right:6px}
.ruler{margin-top:6px}
.new-canvas{margin-top:20px;height:200px;width:1000px}
.food_detail{align-items: center;height:160px;justify-content:space-between;}
.images{height: 120px;;position: absolute;right:20px;padding:20px;align-items: center}
.images image{height:34px}
script:
<script>
export default {
private: {
shortDate: 0,
today: 0,
foodUnitInfo: [],//单位信息
foodInfo: {},//食物信息
activeUnitInfo: {},//当前单位
activeAmount: 0,//当前数量
winWidth: 1000,
showRule: false,
weight_initParams: {
count: 0,
lastX: 0,
initX: 0, //初始x 距离
endX: 0, //结束x 距离
distanceX: 0, //移动距离
distance_X: 0,// 判断用的移动距离
canvasID: 'weight-canvas',
height: 200,
maxScale: 100, //区间数
minScale: 1, //最小刻度值
start_scale: 0, //开始刻度数
markColor: "#FFCC33",
CanCheckStartAndEnd: false //可否选择开始和结束的刻度
}
},
onShow(){
//展示尺子
this.initRule(1);
}
//设置尺子的初始值
initRule(num) {
var me = this;
var unit = me.activeUnitInfo.UnitName
me.weight_initParams.minScale = me.activeUnitInfo.MinVal;
var initNum = 2;
if (num == 0) {
if (unit == '克' || unit == '毫升') {
initNum = 100;
console.log("初始值 " + initNum);
me.weight_initParams.maxScale = 1000;
me.weight_initParams.lastX = initNum;
me.weight_initParams.count = initNum;
me.drawRuler(initNum, 'weight_initParams');
} else {
console.log("初始值 " + initNum);
me.weight_initParams.maxScale = 200;
me.weight_initParams.lastX = initNum;
me.weight_initParams.count = initNum;
me.drawRuler(initNum, 'weight_initParams');
}
} else {
if (unit == '克' || unit == '毫升') {
me.weight_initParams.maxScale = 1000;
} else {
me.weight_initParams.maxScale = 200;
}
initNum = num;
if (num >= 1000) {
initNum = 999;
}
me.weight_initParams.maxScale = 200;
me.weight_initParams.lastX = initNum;
me.weight_initParams.count = initNum;
me.drawRuler(initNum, 'weight_initParams');
},
drawRuler(count, class_initParams) {
var me = this;
count = count < 0 ? 0 : count;
var initParams = eval(' me.' + class_initParams);
var maxScale = initParams.maxScale;
var minScale = initParams.minScale;
var start_scale = initParams.start_scale;
var currentWight = me.$page.query.weight;
if (!initParams.CanCheckStartAndEnd) {
if (count == 0) {
count = 1;
}
if (count == maxScale) {
count = maxScale - 1;
}
}
eval('me.' + class_initParams + '.count= count');
var winWidth = this.winWidth;
count = count - 20.75;
//刻度值数组
const canvas = this.$element(initParams.canvasID) //获取 canvas 组件
const cxt = canvas.getContext('2d') //获取 canvas 绘图上下文
const img = new Image() //新建图像对象
//清空画布
// cxt.clearRect(0, 0, winWidth, initParams.height);
img.src = '../Common/ruler.png' //加载本地图片
//加载成功的回调
img.onload = () => {
//清空画布
cxt.clearRect(0, 0, 1000, initParams.height);
for (var i = 0; i < (maxScale / 10); i++) {
var ynum = (count * -24) + (i * 240);
if (ynum > -100 && ynum < 800) {
cxt.drawImage(img, (count * -24) + (i * 240), 0);
}
if (ynum > 1000) {
break;
}
var num = ((minScale * i * 10) + start_scale).toFixed(0); //刻度尺
cxt.beginPath();
cxt.save();
cxt.font = " small-caps bold 50px aria";
cxt.fillStyle = initParams.color ? initParams.color : "#C1BDB3";
cxt.textAlign = "center";
cxt.textBaseline = "middle";
cxt.fillText(num.toString(), (count * -24) + (i * 240), 160);
cxt.closePath();
// cxt.drawImage(img, (count * -23)+(i*227), 0, 230, 140);
}
if (maxScale % 10 == 0) {
var num = ((maxScale + start_scale) * minScale).toFixed(0);
cxt.beginPath();
cxt.save();
// cxt.font = "50px ALIBABAFont-Bold";
cxt.font = " small-caps bold 50px aria";
cxt.fillStyle = initParams.color ? initParams.color : "#C1BDB3";
cxt.textAlign = "center";
cxt.textBaseline = "middle";
cxt.fillText(num.toString(), (count * -24) + (i * 240), 160);
cxt.closePath();
}
//中心刻度线
this.onceDrawRuler(class_initParams);
},
onceDrawRuler(class_initParams) {
var me = this;
var winWidth = this.winWidth;
var initParams = eval('me.' + class_initParams);
//刻度值数组
var division = winWidth / 50; //每个刻度的距离 分割线
const canvas = this.$element(initParams.canvasID) //获取 canvas 组件
const cxt = canvas.getContext('2d') //获取 canvas 绘图上下文
//画横线
cxt.beginPath();
cxt.strokeStyle = "#EAE5DD";
cxt.lineWidth = 1;
cxt.lineCap = "round";
cxt.moveTo(0, 5);
cxt.lineTo(1200, 5);
cxt.stroke();
cxt.restore();
cxt.closePath();
//中心刻度线
cxt.beginPath();
cxt.save();
cxt.strokeStyle = initParams.markColor;
cxt.lineWidth = 1;
cxt.lineCap = "round";
var height = 200 * Math.sin(Math.PI / 3);//计算等边三角形的高
cxt.moveTo((winWidth / 2), 17); //从A(100,0)开始
cxt.lineTo((winWidth / 2) - 14, 4);//从A(100,0)开始,画到B (0,173)结束
cxt.lineTo((winWidth / 2) + 14, 4); //B(0,173)-C(200,173)
//cxt.fillStyle='#00ff00';//以纯色绿色填充
var grd = cxt.createLinearGradient(0, 0, 200, 0);//使用渐变颜色填充,从(0,0)到(200,0) (左到右)
grd.addColorStop(0, initParams.markColor); //起始颜色
grd.addColorStop(1, initParams.markColor); //终点颜色
cxt.fillStyle = grd; //以上面定义的渐变填充
cxt.fill(); //闭合形状并且以填充方式绘制出来
cxt.stroke();
cxt.restore();
cxt.beginPath();
cxt.save();
cxt.strokeStyle = initParams.markColor;
cxt.lineWidth = 5;
cxt.lineCap = "round";
cxt.moveTo((winWidth / 2), 6);
cxt.lineTo((winWidth / 2), 118);
cxt.stroke();
cxt.restore();
cxt.closePath();
},
//刻度尺 相关逻辑
end(class_initParams) {
var me = this;
var count = eval('this.' + class_initParams + '.count');
var lastX = eval('this.' + class_initParams + '.lastX');
// //目标体重不能跟 现在体重一致
if (class_initParams == 'weight_initParams') {
if (count < me.maxCount && count > me.miniCount) {
var animate_rate = 0;
if (count <= lastX) {
// var diffcount= count-me.miniCount;
// let myFunction = setInterval(() => {
// animate_rate += 0.01;
// me.drawRuler(count +diffcount*animate_rate, class_initParams)
// }, 5)
// setTimeout(function () { clearInterval(myFunction); }, 2000);
count = me.miniCount;
eval('me.' + class_initParams + '.count=count');
} else {
count = me.maxCount;
eval('me.' + class_initParams + '.count=count');
}
if (class_initParams == 'weight_initParams') {
me.drawRuler(count, class_initParams)
}
}
eval('me.' + class_initParams + '.lastX=me.' + class_initParams + '.count');
} else {
eval('me.' + class_initParams + '.lastX=me.' + class_initParams + '.count');
}
},
start(class_initParams, e) {
eval('this.' + class_initParams + '.initX = e._touches[0].pageX;')
},
move(class_initParams, e) {
eval(' this.' + class_initParams + '.endX = e._touches[0].pageX;');
this.moveEvent(class_initParams);
},
moveEvent(class_initParams) {
var me = this;
var initParams = eval('me.' + class_initParams);
var winWidth = this.winWidth;
eval('this.' + class_initParams + '.distanceX= Math.floor((initParams.endX - initParams.initX) / (winWidth / 50));');
initParams = eval('me.' + class_initParams);
if (initParams.distanceX === initParams.distance_X) {
return false;
}
eval('this.' + class_initParams + '.distance_X= initParams.distanceX;');
eval('this.' + class_initParams + '.count = initParams.lastX - initParams.distanceX;');
if (initParams.count >= initParams.maxScale || initParams.count <= 0) {
eval('this.' + class_initParams + '.count = initParams.count >= initParams.maxScale ? initParams.maxScale : 0;');
}
this.drawRuler(eval('this.' + class_initParams + '.count'), class_initParams);
}
}
</script>
代码中
img.src = ‘…/Common/ruler.png’ //加载本地图片
…
cxt.drawImage(img, (count * -24) + (i * 240), 0); //绘制图片
图片如下:
在刚开始实现尺子效果时,我并没有用到图片来画,是用canvas的画笔 line 线条来画的尺子,每滑动一次,就用线条绘制整个尺子效果,很耗性能,画面非常卡顿。后来再做优化的时候,尝试通过借助图片来画,可以流畅运行。
这里贴上的代码,是我从项目里摘出来实现尺子效果的核心代码,并不能直接运行。仅供一个实现思路和流程。