Recently, there was a need to achieve drag-and-drop sorting of pictures. It would have just touched the Wechat applet, tampered with it for a long time, and checked a lot of information, but it was still a lot of confusion, and finally achieved. So I want to record here, hoping to help others, if there is something wrong, or can be improved. Please let me know. Thank you.
Design sketch
(The effect on the real machine is not demonstrated, it's almost the same.)
Implementation ideas
layout
Here we use the moveable-area and moveable-view tags of the Wechat applet.
Moeable-area is a draggable area, which needs to be set in width and height. Since the size of the picture is set dynamically according to the screen, the width of moveable-area is fixed at 100%, and the height is determined by the total height of the uploaded image, so I set the minimum height at the beginning.
The width of moveable-view is the same as that of the picture, and it is also set dynamically. The initial state is hidden, and the picture will be displayed when it is pressed for a long time. When the image is long to sort, record its url and assign it to the image of moveable-view.
<movable-area class="movable-area" style="min-height:{{imageWitdh}}px;height:{{areaHeight}}px"> <!--Picture upload--> <view class="image-choose-container"> <view class="image-item" style="width:{{imageWitdh}}px;height:{{imageWitdh}}px" wx:for="{{images}}" wx:for-item="url" wx:key="url" data-url="{{url}}" data-index="{{index}}" > <image src="{{url}}" mode="aspectFill"></image> <view class="close">X</view> </view> <!--Picture Upload Button--> <view class="add-button" style="width:{{imageWitdh}}px;height:{{imageWitdh}}px" wx:if="{{images.length >= 0 &&images.length < 9}}">+</view> <!--ensure flex layout justify-content: space-between Left alignment of the last line--> <view style="width:{{imageWitdh}}px" class="image-item image-item-temp" wx:if="{{images.length%3==1}}"></view> </view> <movable-view class="movable-view" style="width:{{imageWitdh}}px;height:{{imageWitdh}}px" hidden="{{hidden}}" x="{{x}}" y="{{y}}" direction="all" damping="{{5000}}" friction="{{1}}"> <image src="{{currentImg}}" wx:if="{{currentImg.length>0}}"></image> </movable-view> </movable-area>
Computing width and height js at page initialization
// Calculate picture width _handleComputedImage:function(e){ const windowWidth = app.globalData.systemInfo.windowWidth; const width = windowWidth - 16; const imageWitdh = (width - 16) / 3; this.setData({ imageWitdh }) },
Upload pictures
After uploading the image, we need to change the height of moveable-area.
// Select pictures handleChooseImage: function (e) { let length = this.data.images.length; if (length == 9) { wx.showToast({ title: "Pro, you can only choose nine maps at most.~", icon: "none", duration: 2000 }) return false; } var that = this; wx.chooseImage({ count: 9 - this.data.images.length, sizeType: ['compressed'], //Choose the original or compressed image sourceType: ['album', 'camera'], //Optional Open Access to Albums, Cameras success: res => { let images = that.data.images; for (let i = 0; i < res.tempFilePaths.length;i++){ images.push(res.tempFilePaths[i]); } that.setData({ images },function(){ //Update area after uploading that._handleComputedArea(); }); }, fail: err => console.log(err) }) },
The updated area is calculated as follows, and its height is determined by the view of. image-select-container:
// Calculate the height of movable-area _handleComputedArea:function(e){ let that = this; wx.createSelectorQuery().selectAll('.image-choose-container').boundingClientRect(function (rect) { that.setData({ areaHeight: rect[0].height }) }).exec() },
When deleting pictures, we also need to recalculate the height of moveable-area.
Long press picture
The trigger mechanism for drag-and-drop sorting of images is long press.
- In the long press, we need to calculate the coordinates of each picture (where the coordinates are not fixed, and when your page can be dragged, the coordinates will change) and save them.
- Record the subscripts and URLs of the current image in the image array;
- Display moveable-view, set its x and y values, and assign the url to the child elements below it.
// Calculate the coordinates of each picture _handleComputedPoints(e){ let that = this; var query = wx.createSelectorQuery(); var nodesRef = query.selectAll(".image-item"); nodesRef.fields({ dataset: true, rect: true }, (result) => { that.setData({ pointsArr: result }) }).exec() },
// Long press picture handleLongTap:function(e){ // Calculate the coordinates of each picture this._handleComputedPoints(); this.setData({ currentImg: e.currentTarget.dataset.url, currentIndex: e.currentTarget.dataset.index, hidden: false, flag: true, x: e.currentTarget.offsetLeft, y: e.currentTarget.offsetTop }) },
At this point, by pressing the picture long, the moveable-view (with a border) will appear on the picture.
Mobile pictures
Listen for the catchtouch move event of moveable-view. (The reason why bindhtouchmove is not used is that if the page is slidable during image movement, it will cause the page to slide along with it.) Record the current finger position on the page e.touches[0].pageX and e.touches[0].pageY.
In order to ensure that the picture can move with the finger in the process of moving, the x distance of moveable-view is e.touches[0].pageX of the finger, and the y distance is e.touches[0].pageX - moving distance of scrollbar - image-selection-container of the element distance from the top.
In order to ensure that the picture can always be in the middle of the finger in the process of moving the picture, the width of the picture is subtracted by X and Y respectively. (Comparing two pictures, the location of mouse and moveable-view)
// In the process of moving handleTouchMove:function(e){ let x = e.touches[0].pageX; let y = e.touches[0].pageY; // Firstly, the distance from the top of the current image-select-container is obtained. let that = this; wx.createSelectorQuery().selectAll('.image-choose-container').boundingClientRect(function (rect) { let top = rect[0].top; y = y - that.data.scrollTop - top; that.setData({ x: x - that.data.imageWitdh / 2 > 0 ? x - that.data.imageWitdh / 2:0, y: y - that.data.imageWitdh / 2 > 0 ? y - that.data.imageWitdh / 2:0, }) }).exec() },
// Monitor rolling onPageScroll:function(e){ this.data.scrollTop = e.scrollTop; }
Stop dragging
Listen for the bind touch end event of moveable-view, calculate the current x,y values, compare the subscripts of each picture, find out where it moved, update the array, and finish.
// At the end of the move handleTouchEnd:function(e){ if (!this.data.flag) { // Non-long press return; } let x = e.changedTouches[0].pageX; let y = e.changedTouches[0].pageY - this.data.scrollTop; // The address of each picture const pointsArr = this.data.pointsArr; let data = this.data.images; for (var j = 0; j < pointsArr.length; j++) { const item = pointsArr[j]; if (x > item.left && x < item.right && y > item.top && y < item.bottom) { const endIndex = item.dataset.index; const beginIndex = this.data.currentIndex; //Temporarily save moving target data let temp = data[beginIndex]; //Replace the subscript value of the moving target with the subscript value of the moving target data[beginIndex] = data[endIndex]; //Replace the subscript value of the moving target with beginIndex data[endIndex] = temp; } } this.setData({ images: data, hidden: true, flag: false, currentImg: '' }) },
Finally, attach demo address:
https://github.com/Middletwo-Kid/wechat-drag-image
The article was first published in:
https://juejin.im/post/5d7cbde06fb9a06b3260a25d