完成效果:

Android KE_css


在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); //绘制图片
图片如下:

Android KE_html_02

在刚开始实现尺子效果时,我并没有用到图片来画,是用canvas的画笔 line 线条来画的尺子,每滑动一次,就用线条绘制整个尺子效果,很耗性能,画面非常卡顿。后来再做优化的时候,尝试通过借助图片来画,可以流畅运行。

这里贴上的代码,是我从项目里摘出来实现尺子效果的核心代码,并不能直接运行。仅供一个实现思路和流程。