uniapp怎么實現小程序頁面的自由拖拽功能?下面本篇文章給大家介紹一下uniapp實現小程序頁面自由拖拽組件的方法,希望對大家有所幫助!
先看實現效果:
實現過程
根據查閱文檔,要實現拖拽功能,大概有三種方式:
1.給需要實現拖拽的元素監聽catchtouchmove
事件,動態修改樣式坐標
這種方式最容易想到,通過js監聽觸摸位置動態修改元素坐標。但是拖拽是一個實時性要求非常高的操作,你不能說在這個操作里面去設置節流函數減少setData操作,并且本身每次setData操作也是比較耗性能的,很容易造成拖拽卡頓,這個方案可以首先排除。
2.movable-area + movable-view
movable-area組件的作用是定義一個區域,在這個區域內的movable-view的組件可以被用戶自由的移動,同時movable-view可以輕松設置放大縮小效果。根據組件定義,可以想到它的使用場景大概是在頁面局部區域內對一些元素拖拽縮放,這個與我們想要的在整個頁面進行自由拖拽的需求不符。
3.wxs響應事件
wxs是專門用來解決有頻繁交互的場景,它直接在視圖層運行,免去了視圖層跟邏輯層通信帶來的性能損耗,實現流暢的動畫效果。詳見:wxs響應事件 。根據wxs的使用場景,基本能確定我們要的功能實現應該使用wxs方案。
代碼實現
我們使用的是uniapp框架,查閱uniapp文檔,官方直接提供了一個自由拖拽的代碼案例,鏈接點擊這里。
直接拿官方的代碼示例改造一番,如下:
<template> <view catchtouchmove="return"> <view @click="play" @touchstart="hudun.touchstart" @touchmove="hudun.touchmove" @touchend="hudun.touchend"> <canvas id="lottie-canvas" type="2d" style="width: 88px; height: 102px;"></canvas> </view> </view> </template> <script module="hudun"> var startX = 0 var startY = 0 var lastLeft = 20 var lastTop = 20 function touchstart(event, ins) { ins.addClass('expand') var touch = event.touches[0] || event.changedTouches[0] startX = touch.pageX startY = touch.pageY } function touchmove(event, ins) { var touch = event.touches[0] || event.changedTouches[0] var pageX = touch.pageX var pageY = touch.pageY var left = pageX - startX + lastLeft var top = pageY - startY + lastTop startX = pageX startY = pageY lastLeft = left lastTop = top ins.selectComponent('.movable').setStyle({ right: -left + 'px', bottom: -top + 'px' }) } function touchend(event, ins) { ins.removeClass('expand') } module.exports = { touchstart: touchstart, touchmove: touchmove, touchend: touchend } </script> <script> import lottie from 'lottie-miniprogram' let insList = {} // 存放動畫實例集合 export default { props: { tag: String }, data() { return { isPlay: true, } }, methods: { init() { const query = uni.createSelectorQuery().in(this) query.select('#lottie-canvas').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node const context = canvas.getContext('2d') const dpr = uni.getSystemInfoSync().pixelRatio canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr context.scale(dpr, dpr) lottie.setup(canvas) const ins = lottie.loadAnimation({ loop: true, autoplay: true, path: 'https://usongshu.oss-cn-beijing.aliyuncs.com/data/other/f8780255686b0bb35d25464b2eeea294.json', rendererSettings: { context, }, }) insList[this.tag] = ins setTimeout(() => { this.isPlay = false ins.stop() }, 3000) }) }, play() { const ins = insList[this.tag] if (!this.isPlay) { this.isPlay = true ins.play() setTimeout(() => { this.isPlay = false ins.stop() }, 3000) } } }, beforeDestroy() { delete insList[this.tag] } } </script> <style> .area position fixed right 20px bottom 20px width 88px height 102px z-index 99999 .expand width 100vw height 100vh .movable position absolute </style>
上面代碼是開篇效果圖實現的完整代碼,已經封裝一個單獨的組件。我們要拖拽的是一個canvas元素,用到了lottie動畫庫,點擊時會播放動畫。如果你要實現頁面拖拽的只是一個簡單的按鈕,那代碼量會少很多。如果你要實現的功能跟這個類似,那么針對上面代碼有以下幾點需要值得解釋:
1、我們的需求是在多個頁面需要展示,經過查閱相關資料,是沒法實現在只在一個地方放置組件,然后每個頁面展示,必須每個頁面引入該組件。幸運的是,uniapp支持定義全局小程序組件,可以幫我們減少引入的代碼量。做法如下: 在main.js中定義組件
// 動畫組件 import { HudunAnimation } from '@/components/hudun-animation/index' Vue.component('HudunAnimation', HudunAnimation)
頁面中使用: wxml:
<HudunAnimation tag="index" ref="hudunRef"></HudunAnimation>
// 進入頁面時初始化動畫 mounted() { this.$refs.hudunRef.init() }
2、可以注意到,上面封裝的組件當中,有一個tag屬性,它是用來標識來自哪個頁面的動畫實例。它的存在是由于在組件當中,正常情況下我們可以直接在data中定義一個屬性存放動畫實例,但是經過踩坑發現如果直接這么寫
this.ins = lottie.loadAnimation({})
控制臺會報一個錯誤,是因為lottie.loadAnimation({})返回的對象放置于data中會經過一個JSON.stringfy的過程,在這個過程中不知道什么原因報錯了。為了解決此報錯,改為在組件全局定義一個insList存放動畫實例集合,通過傳入的tag拿到對應的頁面實例,然后調用對應的實例play方法。
頁面穿透及點擊問題
1、在拖拽頁面的時候,會帶動頁面的滾動,解決這個問題很簡單,在area view中添加
catchtouchmove="return"
即可
2、無法點擊拖拽區域頁面按鈕問題。首先我們的拖拽區域是整個頁面,用的是fixed定位覆蓋整個頁面,但是這么一來就會導致蒙層下面的頁面無法響應點擊事件。所以我們需要通過動態設置類名expand,當元素處于拖拽狀態時,我們才將蒙層的區域覆蓋整個頁面,而初始時區域跟拖拽元素保持一致即可。代碼實現見上面完整代碼