微信小程序滑动刻度尺功能

732 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一 效果图

image.png

​ 接到这个需求之后,立马在网络上搜索,发现了好几个。但是都与需求有一定的差别。自己从头写,那是不可能的,改造还是可以的。一个是github.com/mehaotian/w… 这个。下载下来运行了,但满足不了需求,而且发现是使用canvas绘制图片实现的,一大堆未使用过的方法。改造起来特别困难。于是放弃了。那只有继续搜索了。找到了这个blog.csdn.net/txtop/artic… 都看得懂,也能够改。但还是想偷懒,于是去群里问了一句。结果还是没有答案。只有硬着头皮弄了。

想到第一个方法是用一个椭圆去覆盖下面,从视觉上看着是弯曲的,但是左右长度就不等了。

代码

wx-scale.js

// components/heightAndWeight/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    min: {
      type: Number
    },
    max: {
      type: Number
    },
    unit: {
      type: String
    },
    currentNumber: {
      type: Number
    },
    multipleItems: {
      type: Number,
      value: 9
    },
    offset: {
      type: Number,
      value: 4
    }
  },
  observers: {
    "current": function (current) {
      console.log('current-----currentNumber---', current, this.data.currentNumber)
      if (current < this.data.offset) {
        this.setData({
          currentNumber: Math.max(this.data.currentNumber + current - this.data.offset, this.data.minNumber)
        })
      } else if (current > this.data.offset) {
        this.setData({
          currentNumber: Math.min(this.data.currentNumber + current - this.data.offset, this.data.maxNumber)
        })
      }
    },
    "currentNumber": function (currentNumber) {
      console.log('----currentNumber', currentNumber)
      let arr = []
      for (let l = parseInt(this.data.multipleItems / 2) + this.data.offset; l > 0; l--) {
        arr.push(currentNumber - l >= this.data.minNumber ? currentNumber - l : '')
      }
      arr.push(currentNumber)

      for (let l = 1; l <=  parseInt(this.data.multipleItems / 2) + this.data.offset; l++) {
        arr.push(currentNumber + l <= this.data.maxNumber ? currentNumber + l : '')
      }
      console.log('-----arr', arr)

      this.setData({
        arr,
        current: this.data.offset
      })

    }
  },
  attached() {
    this.setData({
      minNumber: this.data.min,
      maxNumber: this.data.max,
      current: 0,
     
      arr: [],
    })
    
  },
  /**
   * 组件的初始数据
   */
  data: {
    minNumber: null,
    maxNumber: null,
    current: 0,
    arr: []
  },

  /**
   * 组件的方法列表
   */
  methods: {
    getCurrent(e) {
      this.setData({
        current: e.detail.current
      })
      //console.log('eeeeeeeeeeeeee', e.detail.current, this.data.currentNumber)
      this.triggerEvent('currentNumber', {
        current: this.data.currentNumber
      })
    }
  }
})

wx-scale.wxml 代码

<view class="mydemo">
  <view class="myshow">
    <text style="color: #4377E9 " ><text class="mytext">{{currentNumber}}</text> {{ unit }}</text>
    <text class="line"></text>
  </view>
 
  <swiper duration="10" class="swiperss" bindchange="getCurrent" display-multiple-items="{{multipleItems}}" easing-function = "linear" current="{{ current }}">
    <swiper-item class="swiperItems" wx:for="{{multipleItems + offset * 2}}" wx:key="index">
      <view wx:if="{{arr[item]!=''}}"   class="{{arr[item]%5==0?'linestwo':'lines'}}"></view>
      <view style="color: #BBBEC3;">{{arr[item]%5==0?arr[item]:''}}</view>
    </swiper-item>

  </swiper>
  <view class="mylineBox circle2">
  </view>
</view>

wx-scale.wxss

/* components/heightAndWeight/index.wxss */
.mydemo {
  height: 255rpx;
  position: relative;
}

.myshow {
  left: 0;
  right: 0;
  top: 5%;
  font-size: 24rpx;
  position: absolute;
  text-align: center;
}
.mytext{
  font-size: 44rpx;font-family: DIN-Bold, DIN;font-weight: bold;color: #4377E9 ;line-height: 54rpx;
}
/* components/heightAndWeight/index.wxss */
.swiperss {
  height: 100%;
  width: 100%;
  margin: 0 auto;
  overflow: visible;
  z-index: 10;
  position: absolute;
  /* border: 1px solid red; */
}
.mylineBox{
  border-bottom: 6rpx solid #EEC40A;
  height: 15rpx;
  border-radius: 0 0 100% 50%/100%;
  position: absolute;
  left: 0;
  width: 100%;
  background: #ffffff;
  opacity: 0.3;
  /* margin-bottom: -6rpx; */
  z-index: 1000;
}
.circle2{
  bottom: -7.5rpx;
}
.swiperItems {
  font-size: 24rpx;
  position: relative;
  margin-top: 74rpx;
  border-bottom: 1px solid #F5F7F9;
  height: 200rpx !important;
  width: calc(570rpx / 19) !important;
  overflow: visible;
  /* border: 1px solid green; */
}

.swiperItems>.lines {
  background-color: #EEC40A;
  /* background-color: green; */
  margin-bottom: 10rpx;
  width: 2rpx;
  height: 65rpx;
  margin-left: 15rpx;
  position: absolute;
  bottom: 3.5rpx;
}

.linestwo {
  margin: 0 auto;
  width: 2rpx;
  height: 100rpx;
  background-color: #EEC40A;
  /* background-color: red; */
  margin-left: 16rpx;
  position: absolute;
  bottom: 3.5rpx;
}

.lines+view {
  font-size: 24rpx;
  font-family: DIN-Regular, DIN;
  font-weight: 400;
  /* color: #D9D9D9; */
  color: red;
  line-height: 30rpx;
  width: 100%;
  text-align: center;
}

.line {
  position: absolute;
  left: 52.5%;/*关键控制数据*/
  top: 64rpx;
  /* transform: translateX(-50%); */
  width: 4rpx;
  height: 120rpx;
  background: #43A3FF;
  box-shadow: 0px 0px 2rpx 0px #43A3FF;
  z-index: 6;
  /* margin-right: 100rpx; */
}

第二个方法是在家里想到的,于是今天来公司实践了。功夫不负,实现了。

思路是左右都连续抬高元素位置,实现视觉上弯曲效果,而且左右高度也一致。与需求完全一致。代码如下

是参考 这个blog.csdn.net/txtop/artic… 还是组件化。

二 组件代码

heightAndWeight.js 代码

// components/heightAndWeight/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    min: {
      type: Number
    },
    max: {
      type: Number
    },
    unit: {
      type: String
    },
    currentNumber: {
      type: Number
    },
    multipleItems: {
      type: Number,
      value: 9
    },
    offset: {
      type: Number,
      value: 4
    }
  },
  observers: {
    "current": function (current) {
      console.log('current-----currentNumber---', current, this.data.currentNumber)
      if (current < this.data.offset) {
        this.setData({
          currentNumber: Math.max(this.data.currentNumber + current - this.data.offset, this.data.minNumber)
        })
      } else if (current > this.data.offset) {
        this.setData({
          currentNumber: Math.min(this.data.currentNumber + current - this.data.offset, this.data.maxNumber)
        })
      }
    },
    "currentNumber": function (currentNumber) {
      console.log('----currentNumber', currentNumber)
      let arr = []
      for (let l = parseInt(this.data.multipleItems / 2) + this.data.offset; l > 0; l--) {
        arr.push(currentNumber - l >= this.data.minNumber ? currentNumber - l : '')
      }
      arr.push(currentNumber)

      for (let l = 1; l <=  parseInt(this.data.multipleItems / 2) + this.data.offset; l++) {
        arr.push(currentNumber + l <= this.data.maxNumber ? currentNumber + l : '')
      }
      console.log('-----arr', arr)

      this.setData({
        arr,
        current: this.data.offset
      })

    }
  },
  attached() {
    this.setData({
      minNumber: this.data.min,
      maxNumber: this.data.max,
      current: 0,
     
      arr: [],
    })
    
  },
  /**
   * 组件的初始数据
   */
  data: {
    minNumber: null,
    maxNumber: null,
    current: 0,
    arr: []
  },

  /**
   * 组件的方法列表
   */
  methods: {
    getCurrent(e) {
      this.setData({
        current: e.detail.current
      })
      //console.log('eeeeeeeeeeeeee', e.detail.current, this.data.currentNumber)
      this.triggerEvent('currentNumber', {
        current: this.data.currentNumber
      })
    }
  }
})

heightAndWeight.json 代码

{
  "component": true,
  "usingComponents": {}
}

heightAndWeight.wxml 代码 很关键,注意看注释说明

<view class="mydemo">
  <view class="myshow">
    <text style="color: #4377E9 " ><text class="mytext">{{currentNumber}}</text> {{ unit }}</text>
    <text class="line"></text>
  </view>
 
  <swiper duration="10" class="swiperss" bindchange="getCurrent" display-multiple-items="{{multipleItems}}" easing-function = "linear" current="{{ current }}">
    <swiper-item class="swiperItems" wx:for="{{multipleItems + offset * 2}}" wx:key="index">
    <!-- <block wx:for="{{(multipleItems + offset * 2+1)/2}}" wx:key="index">
      <view wx:if="{{arr[item]!=''}}"   class="{{arr[item]%5==0?'linestwo':'lines'}}"></view>
    </block> -->
   <block wx:if="{{arr[item]!=''}}">
    <block wx:if="{{arr[item]%5==0}}">
      <block wx:if="{{item<=((multipleItems + offset * 2)+1)/2}}">
      <!-- //这里35是(multipleItems + offset * 2)计算得来的,所以更改样式,需要重新调整 -->
        <view style='height: 100rpx; width: 2rpx; background-color: #EEC40A;margin-left: 16rpx;position: absolute;bottom: {{45-item}}rpx;'>  </view>
        <!-- <text style="color: red;"> {{item}}</text> -->
      </block>
      <block wx:else>
        <view style='height: 100rpx; width: 2rpx; background-color: #EEC40A;margin-left: 16rpx;position: absolute;bottom: {{item+5}}rpx;'>  </view>
       <!-- <text style="color: gold;"> {{item}}</text> -->
      </block>
    </block>
    <block wx:else>
      <block wx:if="{{item<=((multipleItems + offset * 2)+1)/2}}">
        <view style='background-color: #EEC40A;margin-bottom: 10rpx;width: 2rpx;height: 65rpx;margin-left: 15rpx;position: absolute ;bottom: {{35-item}}rpx;'>
      </view>
      <!-- {{item}} -->
      </block>
      <block wx:else>
        <view style='background-color: #EEC40A;margin-bottom: 10rpx;width: 2rpx;height: 65rpx;margin-left: 15rpx;position: absolute ;bottom: {{item}}rpx;'>  </view>
      </block>
    </block>
  </block>
    <!-- <view style='background-color: #D2D6DF;margin-bottom: 10rpx;width: 2rpx;height: 55rpx;margin-left: 15rpx;position: absolute ;bottom: {{35-item}}rpx;'>
    </view> -->
      <!-- <view wx:if="{{arr[item]!=''}}"   class="{{arr[item]%5==0?'linestwo':'lines'}}"></view> -->
      <view style="color: #BBBEC3;">{{arr[item]%5==0?arr[item]:''}}</view>
    </swiper-item>

  </swiper>
  <!-- <view class="mylineBox circle2">
  </view> -->
</view>

heightAndWeight.wxss 样式代码

/* components/heightAndWeight/index.wxss */
.mydemo {
  height: 255rpx;
  position: relative;
}

.myshow {
  left: 0;
  right: 0;
  top: 5%;
  font-size: 24rpx;
  position: absolute;
  text-align: center;
}
.mytext{
  font-size: 44rpx;font-family: DIN-Bold, DIN;font-weight: bold;color: #4377E9 ;line-height: 54rpx;
}
/* components/heightAndWeight/index.wxss */
.swiperss {
  height: 100%;
  width: 100%;
  margin: 0 auto;
  overflow: visible;
  z-index: 10;
  position: absolute;
  /* border: 1px solid red; */
}
.mylineBox{
  border: 1rpx solid #F5F7F9;
  height: 3rpx;
  border-radius: 0 0 100% 50%/100%;
  position: absolute;
  left: 0;
  width: 100%;
  background: #F5F7F9;
  margin-bottom: -6rpx;
  z-index: -1000;
}
.circle2{
  bottom: -1.5rpx;
}
.swiperItems {
  font-size: 24rpx;
  position: relative;
  margin-top: 74rpx;
  border-bottom: 1px solid #F5F7F9;
  height: 200rpx !important;
  width: calc(570rpx / 19) !important;
  overflow: visible;
  /* border: 1px solid green; */
}

.swiperItems>.lines {
  background-color: #D2D6DF;
  /* background-color: green; */
  margin-bottom: 10rpx;
  width: 2rpx;
  height: 55rpx;
  margin-left: 15rpx;
  position: absolute;
  bottom: 0;
}

.linestwo {
  margin: 0 auto;
  width: 2rpx;
  height: 90rpx;
  background-color: #D2D6DF;
  /* background-color: red; */
  margin-left: 16rpx;
  position: absolute;
  bottom: 0;
}

.lines+view {
  font-size: 24rpx;
  font-family: DIN-Regular, DIN;
  font-weight: 400;
  /* color: #D9D9D9; */
  color: red;
  line-height: 30rpx;
  width: 100%;
  text-align: center;
}

.line {
  position: absolute;
  left: 52.5%;/*关键控制数据*/
  top: 64rpx;
  /* transform: translateX(-50%); */
  width: 6rpx;
  height: 170rpx;
  background: #43A3FF;
  box-shadow: 0px 0px 2rpx 0px #43A3FF;
  z-index: 600;
  /* margin-right: 100rpx; */
}
三 调用

页面index

index.js 代码

//index.js
//获取应用实例
const app = getApp();

Page({
    data: {
      ageCurrentNumber:30,
      tempData:0
    },
    onLoad: function (options) {

    },
    setNum(e){
    let that=this
      this.setData({
        ageCurrentNumber:that.tempData
      })
     // this.getInputValue(10)
    },
    getInputValue(e) {
      console.log(e,"dss")
      this.setData({
        ageCurrentNumber: e.detail.value
      })
      this.tempData=e.detail.value;
    },
    getCurrentNumberAge(e){  //年龄
      console.log('获取当前current值年龄',e,e.detail.current)
  
      let result = e.detail.current;
      this.setData({
        age:result ,
        ageCurrentNumber:result
      })
    },

})

index.json 代码

{
  "navigationStyle":"default",
  "navigationBarBackgroundColor": "#fff",
  "navigationBarTitleText": "刻度尺",
  "navigationBarTextStyle": "black",
  "disableScroll": true,
  "usingComponents": {
       "height-weight3": "/components/heightAndWeight",
       "scale": "/components/wx-scale"
  }
}

调用页面 index.wxml

<view class="mypage-demo">


  <!-- <button type="default" plain="true" bindtap="setNum"  >按钮</button> -->
  <view class="my-input-text">
    <view class="my-label">
      选择时长
    </view>
    <view class="my-value">
      <input  class="demo-value" type="number" maxlength="10"  name='price' placeholder="输入时长分钟"  bindinput='getInputValue'/> <text class="demo-unit"></text>
    </view>

  </view>
  <view class="flex" style="flex-wrap:wrap;margin-top: 20rpx">
  <!-- 如果调整页面padding 这里offset和multipleItems需要重新调整,否则会出现数值错位 -->
    <height-weight3 offset="5" min="0" max="9999" unit="分" multipleItems="25" currentNumber="{{ ageCurrentNumber }}" bind:currentNumber="getCurrentNumberAge"></height-weight3>
  </view>
  <view class="demo-tips">
    有效期半年
  </view>
  <view class="demo-buy-card">
  <view class="buy-left">
  <view class="buy-num">合计: <text class="buy-value"> ¥{{ageCurrentNumber}}</text></view>
  <view class="buy-time">洗车时长{{ageCurrentNumber}}分钟</view>
  </view>
  <view class="buy-right">
  <view>立即购买</view>
  </view>
  </view>
  <view class="flex" style="flex-wrap:wrap;margin-top: 20rpx">
    <scale offset="5" min="0" max="9999" unit="分" multipleItems="25" currentNumber="{{ ageCurrentNumber }}" bind:currentNumber="getCurrentNumberAge"></scale>
  </view>
</view>

样式 index.wxss

/*questions.wxss*/
page{
	background: #ffffff;
}
.mypage-demo{
  margin: 20rpx 0;
  padding: 15rpx;
}
.my-input-text{
  margin: 50rpx 10rpx;
  padding: 40rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.my-label{
  width: 150rpx;
}
.my-value{
  flex: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.demo-value{
  flex: 7;
}
.demo-unit{
  flex:3;
  color: red;
}
::-webkit-scrollbar {
  width: 0;
  height: 0;
  color: transparent;
  display: none;
}
.demo-tips{
  margin: 40rpx;
  justify-content: center;
  align-items: center;
  text-align: center;
  display: flex;
  font-size: 40rpx;
}
.demo-buy-card{
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 40rpx;
  padding: 20rpx;
  border: 2rpx solid #ECEDEE;
  border-radius: 40rpx;
}
.buy-left{
  flex:7;
}
.buy-num{
   margin:10rpx 0;
}
.buy-value{
  font-size: 36rpx;
  font-weight: bold;
}
buy-time-num{
  margin:10rpx 0;
}
.buy-right{
  flex:3;
}