亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

微信小程序圖片上傳組件實(shí)現(xiàn)圖片拖拽排序

 更新時間:2022年03月23日 16:07:58   作者:恪愚  
最近在寫小程序過程中遇到一個拖拽排序需求,上網(wǎng)一頓搜索未果,遂自行實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于微信小程序圖片上傳組件實(shí)現(xiàn)圖片拖拽排序的相關(guān)資料,需要的朋友可以參考下

引言

圖片上傳組件是一個組件庫目前來看必不可少的功能了。筆者近日給自己開源的toy工具庫也添加了這一功能。相比原生和大部分組件庫來說,它不僅支持長按提示刪除,還能夠支持圖片的拖拽排序,很是nice!

(也是為了畢設(shè)時身邊同學(xué)能夠更快上手小程序,更加將中心側(cè)重于邏輯和設(shè)計??)

本文我將繼續(xù)介紹組件的設(shè)計思路:

首先來看效果

對于組件內(nèi)部來說。筆者提供了一個參數(shù)去讓開發(fā)者決定是否應(yīng)該在場景中支持拖動排序。這里略去這些無關(guān)代碼。

拖拽排序功能使用了微信小程序提供的movable-area組件(標(biāo)簽,但小程序也是封裝了HTML,所以以原生組件代稱)。它相當(dāng)于提供了一個可滑動區(qū)域,在此區(qū)域內(nèi)的movable-view組件內(nèi)容可以任意排列。其效果就相當(dāng)于window中的“桌面圖標(biāo)非對齊”效果 —— 記住這個描述,它和下面的內(nèi)容聯(lián)系緊密!

其中主要的兩個參數(shù)是:

  • x:定義x軸方向的偏移,如果x的值不在可移動范圍內(nèi),會自動移動到可移動范圍;改變x的值會觸發(fā)動畫;
  • y:定義y軸方向的偏移,如果y的值不在可移動范圍內(nèi),會自動移動到可移動范圍;改變y的值會觸發(fā)動畫;

嗯,可以知道,其內(nèi)部是通過 js 觸發(fā)的動畫。而且可能是requestAnimeFrame API。

組件設(shè)計

知道了所用標(biāo)簽,接下來就該正式開發(fā)了。但是你會發(fā)現(xiàn),這里其實(shí)有兩種使用方式:

對每個元素使用movable-view包裹,讓他們可以隨意拖拽位置:

<view class="container">
    <movable-area style="width: 100%;height: auto;">
        <view class="image-list">
            <!-- 顯示圖片 -->
            <block wx:if="{{yMovable}}">
                <movable-view x="{{x}}" y="{{y}}" direction="all" inertia damping="{{5000}}" friction="{{1}}" disabled="{{disabled}}" wx:for="{{images}}" wx:key="{{item.index}}">
                    <view class="image-wrap image-bg {{(images.length > 2) ? 'image-flex' : ''}}" id="{{item.index}}" data-index='{{index}}' bindlongpress='onShowMenu' bindtouchstart='touchs' bindtouchend='touchend' bindtouchmove='touchm'>
                        <image class="image" src="{{item.img}}" mode="aspectFill" bind:tap="onPreviewImage" data-imgsrc="{{item.img}}"></image>
                        <i class="iconfont icon-delete" wx:if="{{showMenuImg}}" bind:tap="onDelImage" data-index="{{index}}"></i>
                    </view>
                </movable-view>
            </block>
            
            <!-- 選擇圖片 -->
            <view class="image-wrap selectphoto" bind:tap="onChooseImage" hidden="{{!selectPhoto}}">
                <i class="iconfont icon-jiashang"></i>
            </view>
        </view>
    </movable-area>
</view>

圖片只是展示;單獨(dú)設(shè)置一個元素,在長按圖片時顯示,其值為當(dāng)前選中的圖片,拖拽的是這個元素,到達(dá)目標(biāo)位置后消失,圖片列表重新排序。

<view class="container">
    <movable-area style="width: 100%;height: auto;">
        <view class="image-list">
            <!-- 顯示圖片 -->
            <block wx:if="{{yMovable}}">
                <block wx:for="{{images}}" wx:key="{{item.index}}">
                    <view class="image-wrap image-bg {{(images.length > 2) ? 'image-flex' : ''}}" id="{{item.index}}" data-index='{{index}}' bindlongpress='onShowMenu' bindtouchstart='touchs' bindtouchend='touchend' bindtouchmove='touchm'>
                        <image class="image" src="{{item.img}}" mode="aspectFill" bind:tap="onPreviewImage" data-imgsrc="{{item.img}}"></image>
                        <i class="iconfont icon-delete" wx:if="{{showMenuImg}}" bind:tap="onDelImage" data-index="{{index}}"></i>
                    </view>
                </block>
                <movable-view x="{{x}}" y="{{y}}" direction="all" inertia damping="{{5000}}" friction="{{1}}" disabled="{{disabled}}">
                    <view class='image-wrap image-check' style="z-index: 3;" hidden='{{hidden}}'>
                        <image class="image" src="{{doubleImg}}" mode="aspectFill"></image>
                    </view>
                </movable-view>
            </block>
            
            <!-- 選擇圖片 -->
            <view class="image-wrap selectphoto" bind:tap="onChooseImage" hidden="{{!selectPhoto}}">
                <i class="iconfont icon-jiashang"></i>
            </view>
        </view>
    </movable-area>
</view>

第一種方式的優(yōu)勢在于:可以有更加“真實(shí)”的效果。這里的真實(shí)意為重新排列時也有滑動的動畫效果。但是帶來的性能損耗也是極大的,你只能盡力調(diào)控各種數(shù)據(jù)來讓顯示更加“跟手”一些。但是基于此,你可以通過js計算達(dá)到像QQ空間那樣的實(shí)時排列效果!

第二種方式的優(yōu)勢在于:性能開銷相對小一些。但展示效果更像web而非APP(這兩個的區(qū)別你應(yīng)該是知道的)。

當(dāng)前版本中,筆者采用的是第二種方式。其關(guān)鍵 js 代碼如下:

const MAX_IMG_NUM=9;
Component({
  /**
   * 組件的屬性列表
   */
  properties: {
    yMovable:{
      type:Boolean,
      value:false
    },
  },


  /**
   * 組件的初始數(shù)據(jù)
   */
  data: {
    images:[],
    selectPhoto:true,
    showMenuImg: false,
    flag: false,
    hidden:true,
    x:0,
    y:0,
    disabled: true,
    elements:[],
    doubleImg: ""
  },

  /**
   * 組件的方法列表
   */
  methods: {
  	//長按事件
    onShowMenu(e){
        const detail = e.currentTarget;
        if(!this.data.showMenuImg) {
          // 使手機(jī)振動15ms
          wx.vibrateShort();
        }
        this.setData({
            showMenuImg: true
        })
        if(this.properties.yMovable) {
            this.setData({
                x: detail.offsetLeft+5,
                y: detail.offsetTop,
                hidden: false,
                flag:true,
                doubleImg: this.data.images[detail.dataset.index].img
            })
        }
    },
    //觸摸開始
    touchs:function(e){
        this.setData({
          beginIndex:e.currentTarget.dataset.index
        })
    },
    //觸摸結(jié)束
    touchend:function(e){
        if (!this.data.flag) {
          return;
        }
        const x = e.changedTouches[0].pageX
        const y = e.changedTouches[0].pageY
        const list = this.data.elements;
        let data = this.data.images
        for(var j = 0; j<list.length; j++){
          const item = list[j];
          if(x>item.left && x<item.right && y>item.top && y<item.bottom){
            const endIndex = item.dataset.index;
            const beginIndex = this.data.beginIndex;
            //向后移動
            if (beginIndex < endIndex) {
              let tem = data[beginIndex];
              for (let i = beginIndex; i < endIndex; i++) {
                data[i] = data[i + 1]
              }
              data[endIndex] = tem;
            }
            //向前移動
            if (beginIndex > endIndex) {
              let tem = data[beginIndex];
              for (let i = beginIndex; i > endIndex; i--) {
                data[i] = data[i - 1]
              }
              data[endIndex] = tem;
            }
    
            this.setData({
                images: data
            })
            this.initImg(this.triggerMsg(data, "sort-img"))
          }
        }
        this.setData({
          hidden: true,
          flag: false
        })
    },
    //滑動
    touchm:function(e){
        if(this.data.flag){
          const x = e.touches[0].pageX
          const y = e.touches[0].pageY
          this.setData({
            x: x - 75,
            y: y - 45
          })
        }
    },
    
    //選擇圖片
    onChooseImage(){
        let images = this.data.images;
        let imageLen = images.length;
        let max=MAX_IMG_NUM-imageLen;
        wx.chooseImage({
          count:max,
          sizeType:['original','compressed'],
          sourceType:['album','camera'],
          success: (res) => {
            max-=res.tempFilePaths.length;
            let _images = images.map(item => {
                return item.img
            })
            images = _images.concat(res.tempFilePaths)
            for(let i=0;i<images.length;i++) {
                images[i] = {
                    img: images[i],
                    index: i+1
                }
            }
            this.setData({
              selectPhoto:max<=0?false:true,
              images,
              showMenuImg: false
            })
            this.triggerMsg(images, "choose-img")
            if(this.properties.yMovable) {
                this.initImg()
            }
          },
          fail:(res)=>{
          }
        })
    },
	
	// 初始化位置信息
    initImg(fn=function(){}) {
        let query = wx.createSelectorQuery().in(this);
        let nodesRef = query.selectAll(".image-bg");
        nodesRef.fields({
            dataset: true,
            rect:true
        },(result)=>{
            this.setData({
                elements: result;
                fn();
            })
        }).exec()
    },
    
    //刪除
    onDelImage(event){
        let images = this.data.images;
        images.splice(event.target.dataset.index,1)
        this.setData({
          images
        })
        this.initImg(this.triggerMsg(images, "delete-img"))
        if(images.length== MAX_IMG_NUM-1){
          this.setData({
            selectPhoto:true
          })
        }
    },

    triggerMsg(images, key) {
      this.triggerEvent('chooseImg', {
        images: images.map(item => {
            return item.img
        }),
        key: key
      })
    },
  }
})

上面代碼中最重要的就是initImg函數(shù)的這段代碼!它用來獲取wxml節(jié)點(diǎn)的相關(guān)屬性!fields API的參數(shù)及默認(rèn)值有:

id:false,//是否返回節(jié)點(diǎn)id
rect:fasle,//是否返回節(jié)點(diǎn)布局位置
dataset: true,//返回數(shù)據(jù)集
size: true,//返回寬高
scrollOffset: true,//返回 scrollLeft,scrollTop
properties: ['scrollX', 'scrollY'],//監(jiān)聽屬性名
computedStyle: ['margin', 'backgroundColor']//此處返回指定要返回的樣式名

這個API的調(diào)用是后面定位的關(guān)鍵,它必須放在獲取到圖片數(shù)組之后執(zhí)行(不管同步還是異步的)。也是第二種方法和第一種方法的區(qū)別之處 —— 第一種方法是純 js 計算實(shí)時位置。所以它需要在結(jié)束后進(jìn)行排序。

這時候問題就來了:像本文這種場景,同時有 x 和 y 兩個方向的位置,sort將會極其復(fù)雜,而且sort本身的性能將會被內(nèi)部繁雜的代碼死死拖住。這就是上面說第一種方法性能問題的原因所在。

但是本文這種方法將sort簡化為當(dāng)前拖動元素和目標(biāo)位置圖片兩個物體的四個方向判斷,也就是經(jīng)典“小球撞墻”臨界問題。這也是其優(yōu)勢所在。

另一個需要注意的地方就是 touchm函數(shù)中的setData。這里面進(jìn)行的是拖拽元素位置改變,也就是“跟手率”(我自己編的)??梢詫Υ藬?shù)值進(jìn)行微調(diào)來讓效果更加nice一些。

使用方式

首先在json文件中進(jìn)行組件引入:

{
  "usingComponents": {
    "y-img":"/components/yImg/index"
  }
}

然后再wxml中:

<view class="container">
	<y-img bind:chooseImg="chooseImg"></y-img>
	<!--或:-->
    <y-img yMovable bind:chooseImg="chooseImg"></y-img>
</view>
chooseImg(e) {
      console.log(e.detail)
},

GitHub地址:https://github.com/1314mxc/yunUI#img,歡迎使用、查看和star!

總結(jié)

到此這篇關(guān)于微信小程序圖片上傳組件實(shí)現(xiàn)圖片拖拽排序的文章就介紹到這了,更多相關(guān)微信小程序圖片拖拽排序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論