微信小程序 vue 滑块验证码

文章讲述了如何将一个基于Vue的滑块验证码组件修改以适应微信小程序的环境。主要修改点包括替换document为wx.createSelectorQuery来获取DOM,以及使用createImage()创建图片。代码示例展示了在Vue组件和小程序中对应的实现差异,包括事件处理和DOM操作。最后,文章总结了主要冲突在于小程序与Web的API差异,但通过学习和调整可以解决这些问题。
摘要由CSDN通过智能技术生成

前提:之前写的vue版滑块验证码,可以在h5和web使用,但是在微信小程序运行就有问题。因此进行一系列的修改

一、vue版滑块验证码实现地址

vue 图片拖拽验证码_周皮皮皮皮皮皮的博客-CSDN博客

二、微信小程序遇到问题

1. 小程序是没有window和document对象,所以要把之前代码中的document改成wx.createSelectorQuery()去获取dom,记住每一个dom都要写一遍wx.createSelectorQuery

2. 创建图片用createImage()

三、代码展示

1. code.vue(验证码组件)

<template>
  <div id="slideVerify" class="slide-verify" :style="widthlable" onselectstart="return false;">
    <canvas ref="canvas" type="2d" id="myCanvas" />
    <canvas ref="block" type="2d" id="myBlock" class="slide-verify-block" :style="{ left: sliderLeft }" />
    <van-icon class="slide-verify-refresh-icon el-icon-refresh" name="replay" @click="refresh" />
    <div class="slide-verify-info" :class="{ fail: fail, show: showInfo }" @click="refresh">
      {{ infoText }}
    </div>
    <div
      class="slide-verify-slider"
      :style="widthlable"
      :class="{
        'container-active': containerActive,
        'container-success': containerSuccess,
        'container-fail': containerFail
      }"
    >
      <div class="slide-verify-slider-mask" :style="{ width: sliderMaskWidth }">
        <div
          class="slide-verify-slider-mask-item"
          :style="{ left: sliderLeft }"
          @mousedown="sliderDown"
          @touchstart="touchStartEvent"
          @touchmove="touchMoveEvent"
          @touchend="touchEndEvent"
        >
          <div class="slide-verify-slider-mask-item-icon el-icon-s-unfold"></div>
        </div>
      </div>
      <span class="slide-verify-slider-text">{{ sliderText }}</span>
    </div>
  </div>
</template>
<script>
function sum(x, y) {
  return x + y
}

function square(x) {
  return x * x
}
export default {
  name: 'SlideVerify',
  props: {
    // block length
    l: {
      type: Number,
      default: 42
    },
    // block radius
    r: {
      type: Number,
      default: 10
    },
    // canvas width
    w: {
      // 背景图宽
      type: [Number, String],
      default: 350
    },
    // canvas height
    h: {
      // 背景图高
      type: [Number, String],
      default: 200
    },
    // canvas width
    sw: {
      // 小图宽
      type: [Number, String],
      default: 50
    },
    // canvas height
    sh: {
      type: [Number, String],
      default: 50
    },
    // block_x: {
    //   type: Number,
    //   default: 155
    // },
    blocky: {
      // 小图初始的垂直距离
      type: [Number, String],
      default: 20
    },
    sliderText: {
      type: String,
      default: 'Slide filled right'
    },
    imgurl: {
      // 大图地址
      type: String,
      default: ''
    },
    miniimgurl: {
      // 小图地址
      type: String,
      default: ''
    },
    fresh: {
      type: Boolean,
      default: false
    }
  },

  data() {
    return {
      containerActive: false, // container active class
      containerSuccess: false, // container success class
      containerFail: false, // container fail class
      canvasCtx: null,
      blockCtx: null,
      block: null,
      canvasStr: null,
      L: this.l + this.r * 2 + 3, // block real lenght
      img: undefined,
      originX: undefined,
      originY: undefined,
      isMouseDown: false,
      trail: [],
      widthlable: 'width: 300px;',
      sliderLeft: 0, // block right offset
      sliderMaskWidth: 0, // mask width
      dialogVisible: false,
      infoText: '验证成功',
      fail: false,
      showInfo: false
    }
  },
  watch: {
    fresh(val) {
      if (val) {
        this.init()
      }
    }
  },
  mounted() {
    // 随机生成数this.block_x =
    this.init()
  },
  methods: {
    init() {
      this.initDom()
    },
    initDom(h) {
      const that = this
      // 创建背景
      const query = wx.createSelectorQuery()
      query.select('#myCanvas')
      .fields({ node: true, size: true })
      .exec((res) => {
        that.canvasStr = res[0].node;
        that.canvasCtx = that.canvasStr.getContext("2d");
        // 创建图片对象
        let bgImage = that.canvasStr.createImage();
        bgImage.src = that.imgurl;
        bgImage.onload = () => {
          // 画出背景
          that.canvasCtx.drawImage(bgImage, 0, 0)
        }
      })
      // 创建移动模块
      const query1 = wx.createSelectorQuery()
      query1.select('#myBlock')
      .fields({ node: true, size: true, rect: true })
      .exec((res1) => {
        that.block = res1[0].node;
        // 获取canvas 2d绘图上下文
        that.blockCtx = that.block.getContext("2d");
        // 创建图片对象
        let bgImage1 = that.block.createImage();
        var blocky = h || that.blocky
        bgImage1.src = that.miniimgurl;
        bgImage1.onload = () => {
          // 画出背景
          that.blockCtx.drawImage(bgImage1, 0, blocky)
        }
      })
    },
    // 刷新
    refresh() {
      this.$emit('refresh')
    },
    sliderDown(event) {
      this.originX = event.clientX
      this.originY = event.clientY
      this.isMouseDown = true
    },
    touchStartEvent(e) {
      this.originX = e.touches[0].pageX
      this.originY = e.touches[0].pageY
      this.isMouseDown = true
    },
    touchMoveEvent(e) {
      if (!this.isMouseDown) return false
      const moveX = e.touches[0].pageX - this.originX
      const moveY = e.touches[0].pageY - this.originY
      if (moveX < 0 || moveX + 38 >= this.w) return false
      this.sliderLeft = moveX + 'px'
      const blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX
      this.containerActive = true
      this.sliderMaskWidth = moveX + 'px'
      this.trail.push(moveY)
    },
    touchEndEvent(e) {
      if (!this.isMouseDown) return false
      this.isMouseDown = false
      if (e.mp.changedTouches[0].pageX === this.originX) return false
      this.containerActive = false
      this.verify()
    },
    verify() {
      const arr = this.trail // drag y move distance
      const average = arr.reduce(sum) / arr.length // average
      const deviations = arr.map(x => x - average) // deviation array
      const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length) // standard deviation
      const left = parseInt(this.sliderLeft)
      this.$emit('success', left, stddev)
    },
    reset(h) {
      this.containerActive = false
      this.containerActive = false
      this.containerSuccess = false
      this.containerFail = false
      this.sliderLeft = 0
      this.sliderMaskWidth = 0
      this.canvasCtx.clearRect(0, 0, this.w, this.h)
      this.blockCtx.clearRect(0, 0, this.w, this.h)
      this.fail = false
      this.showInfo = false
      this.containerFail = false
      this.containerSuccess = false
      this.initDom(h)
    }
  }
}
</script>
<style scoped>
.slide-verify {
  position: relative;
  width: 100%;
  overflow: hidden;
}

.slide-verify-block {
  position: absolute;
  left: 0;
  top: 0;
}

.slide-verify-refresh-icon {
  position: absolute;
  right: 10px;
  top: 0;
  width: 34px;
  height: 34px;
  cursor: pointer;
  content: '刷新';
  font-size: 22px;
  line-height: 34px;
  text-align: center;
  font-weight: bold;
  color: #3fdeae;
  /* background: url("../../assets/move/icon_light.png") 0 -437px; */
  background-size: 34px 471px;
}
.slide-verify-refresh-icon:hover {
  transform: rotate(180deg);
  transition: all 0.2s ease-in-out;
}

.slide-verify-slider {
  position: relative;
  text-align: center;
  width: 310px;
  height: 40px;
  line-height: 40px;
  margin-top: 15px;
  background: #f7f9fa;
  color: #45494c;
  border: 1px solid #e4e7eb;
}

.slide-verify-slider-mask {
  position: absolute;
  left: 0;
  top: 0;
  height: 40px;
  border: 0 solid #1991fa;
  background: #d1e9fe;
}

.slide-verify-info {
  position: absolute;
  top: 170px;
  left: 0;
  height: 30px;
  width: 350px;
  color: #fff;
  text-align: center;
  line-height: 30px;
  background-color: #52ccba;
  opacity: 0;
}
.slide-verify-info.fail {
  background-color: #f57a7a;
}
.slide-verify-info.show {
  animation: hide 1s ease;
}
@keyframes hide {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 0.9;
  }
}
.slide-verify-slider-mask-item {
  position: absolute;
  top: 0;
  left: 0;
  width: 38px;
  height: 38px;
  background: #fff;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  transition: background 0.2s linear;
}

.slide-verify-slider-mask-item:hover {
  background: #1991fa;
}

.slide-verify-slider-mask-item:hover .slide-verify-slider-mask-item-icon {
  background-position: 0 -13px;
}

.slide-verify-slider-mask-item-icon {
  position: absolute;
  top: 9px;
  left: 7px;
  width: 14px;
  height: 12px;
  content: '法币';
  font-size: 22px;
  color: #ddd;
  /* text-align: center;
    line-height: 12px; */
  /* background: url("../../assets/move/icon_light.png") 0 -26px; */
  /* background-size: 34px 471px; */
}
.container-active .slide-verify-slider-mask-item {
  height: 38px;
  top: -1px;
  border: 1px solid #1991fa;
}

.container-active .slide-verify-slider-mask {
  height: 38px;
  border-width: 1px;
}

.container-success .slide-verify-slider-mask-item {
  height: 38px;
  top: -1px;
  border: 1px solid #52ccba;
  background-color: #52ccba !important;
}

.container-success .slide-verify-slider-mask {
  height: 38px;
  border: 1px solid #52ccba;
  background-color: #d2f4ef;
}

.container-success .slide-verify-slider-mask-item-icon {
  background-position: 0 0 !important;
}

.container-fail .slide-verify-slider-mask-item {
  height: 38px;
  top: -1px;
  border: 1px solid #f57a7a;
  background-color: #f57a7a !important;
}

.container-fail .slide-verify-slider-mask {
  height: 38px;
  border: 1px solid #f57a7a;
  background-color: #fce1e1;
}

.container-fail .slide-verify-slider-mask-item-icon {
  top: 14px;
  background-position: 0 -82px !important;
}

.container-active .slide-verify-slider-text,
.container-success .slide-verify-slider-text,
.container-fail .slide-verify-slider-text {
  display: none;
}
</style>

2. 引用组件

<template>
  <div>
    <van-button class="b-btn" type="info" size="small" @click="handleLogin">登 录</van-button>
    <div v-show="isShowCode" class="box-code">
      <div class="title">
        <span>请完成安全验证</span>
        <span class="close" @click="isShowCode = false">×</span>
      </div>
      <Captcha
        ref="dialogopen"
        :l="42"
        :r="10"
        :w="catcha.w"
        :h="catcha.h"
        :blocky="catcha.blocky"
        :imgurl="catcha.imgurl"
        :miniimgurl="catcha.miniimgurl"
        :slider-text="catcha.text"
        @success="onSuccess"
        @fail="onFail"
        @refresh="onRefresh"
      />
    </div>
  </div>
</template>
<script>
import Captcha from './code'
export default {
  components: {
    Captcha
  },
  data() {
    return {
      isShowCode: false,
      catcha: {
        blocky: 0,
        imgurl: '',
        miniimgurl: '',
        text: '向右滑动完成拼图',
        h: 200,
        w: 350,
        sh: 45,
        sw: 55,
        modifyImg: '',
        unionKey: ''
      } // 图片验证码传值
    }
  },
  methods: {
    handleLogin() {
      if (!this.userInfo.ksh || !this.userInfo.idcard) {
        Toast.fail('请填写账号密码')
        return
      }
      this.isShowCode = true
      this.getImageVerifyCode()
    },
    // 获取图形验证码
    getImageVerifyCode() {
      getPicCode().then(res => {
        if (res && res.data) {
          var imgobj = res.data
          this.catcha.blocky = imgobj.y
          this.catcha.imgurl = 'data:image/png;base64,' + imgobj.shadeImage
          this.catcha.miniimgurl = 'data:image/png;base64,' + imgobj.cutoutImage
          this.catcha.unionKey = imgobj.unionKey
        }
        this.$nextTick(() => {
          if (this.$refs.dialogopen) {
            this.$refs.dialogopen.reset(imgobj.y)
          }
        })
      })
    },
    onSuccess(val) {
      this.login({
        x: val,
        y: this.catcha.blocky,
        unionKey: this.catcha.unionKey
      })
    },
    onFail() {
      console.log('失败')
    },
    // 刷新
    onRefresh() {
      this.catcha.imgurl = ''
      this.catcha.miniimgurl = ''
      this.getImageVerifyCode()
    }
  }
}
</script>
<style>
page {
  background: #cce2fe;
}
</style>
<style scoped>
.item-login {
  padding: 25px 0 0;
  position: relative;
}
.lable-item-login {
  padding: 0 15px 20px;
}
.pgo-top {
  margin-bottom: 5px;
}
.pgo-top-img {
  width: 100%;
}
.b-parent {
  margin: 20px 15px 40px;
}
.box-login {
  position: fixed;
  top: 188px;
  left: 0;
  right: 0;
  border: solid 12px #a5cefc;
  margin: 0 45px;
  background: #fff;
}
.login-top {
  height: 59px;
  width: 172px;
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -20%);
  z-index: 10;
}
.pt50 {
  padding-top: 60px;
}
.pic-code{
  position: absolute;
  width: 80px;
  height: 30px;
  right: 20px; 
  bottom: 5px;
  z-index: 10;
}
.box-code {
  position: fixed;
  left: 50%;
  top: 50%;
  width: 300px;
  transform: translate(-50%, -50%);
  background-color: #fff;
  border-radius: 3px;
  overflow: hidden;
  padding-bottom: 10px;
}
.box-code .title {
  font-size: 14px;
  padding: 5px;
  font-weight: bold;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.box-code .title .close {
  font-size: 20px;
}
.box-code > .content {
  margin: 0 auto;
}
</style>

四、总结

主要是小程序的部分写法和web的写法起冲突了,了解后就可以了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 可以使用腾讯地图的开放平台提供的 JavaScript API 实现在微信小程序中插入地图。以下是基本的步骤: 1. 在腾讯地图开放平台注册账号并创建应用,获取 key 值。 2. 在微信小程序中使用 npm 安装腾讯地图 JavaScript API: ``` npm install --save @tencent/maps ``` 3. 在小程序页面的 js 文件中引入腾讯地图 API: ``` import QQMapWX from '@tencent/maps' const qqmapsdk = new QQMapWX({ key: '你的key值' }) ``` 4. 在小程序页面的 wxml 文件中添加地图组件: ``` <map id="myMap" longitude="{{longitude}}" latitude="{{latitude}}" scale="{{scale}}" markers="{{markers}}" show-location="{{true}}"></map> ``` 5. 在小程序页面的 js 文件中设置地图的参数和标记点: ``` Page({ data: { longitude: 113.324520, latitude: 23.099994, scale: 14, markers: [{ iconPath: '/images/location.png', id: 0, latitude: 23.099994, longitude: 113.324520, width: 50, height: 50 }] }, onLoad: function () { // 获取当前位置并设置地图中心点为当前位置 wx.getLocation({ type: 'gcj02', success: (res) => { this.setData({ longitude: res.longitude, latitude: res.latitude }) } }) } }) ``` 这样就可以在微信小程序中插入腾讯地图了。详细的 API 文档可以参考腾讯地图开放平台的官方文档。 ### 回答2: 在微信小程序中,可以使用Vue插件来添加地图功能。首先,我们需要选择一个合适的地图插件,例如腾讯地图插件。 1. 在小程序的项目目录下创建一个新的文件夹,命名为"plugins"。 2. 在"plugins"文件夹中,可以将腾讯地图的插件文件(例如tencent-map.js)放置进来。 3. 根据微信官方文档的要求,在小程序的"app.json"文件中声明这个插件: ``` "plugins": { "tencentMap": { "version": "版本号", "provider": "提供者" } } ``` 4. 在Vue组件中引入腾讯地图插件,可以在组件的生命周期钩子"mounted"中引入地图: ```javascript mounted() { wx.getLocation({ type: 'gcj02', // 使用国测局坐标 success: (res) => { let latitude = res.latitude let longitude = res.longitude // 使用Vue插件,在页面中插入地图 let map = this.$plugins.tencentMap.createMapContext('map', { latitude: latitude, longitude: longitude, markers: [{ // 添加标记点 id: 0, latitude: latitude, longitude: longitude, title: '我的位置' }] }) map.showCallout({ // 显示标记点的气泡窗口 markerId: 0, zIndex: 1, content: '我的位置' }) }, fail: (err) => { console.log('获取位置失败', err) } }) } ``` 5. 在Vue组件的模板中添加一个"canvas"标签,用于显示地图: ```html <template> <view> <canvas id="map"></canvas> </view> </template> ``` 通过以上步骤,我们可以在微信小程序中使用Vue插件来插入地图,并设置地图的参数如经纬度、标记点等。当然,还可以根据具体需求添加更多的地图功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值