当前位置: 首页 > news >正文

yunUI组件库解析:图片上传与排序组件yImgPro

yunUI是笔者开源的微信小程序功能库。目前其中包含了一些复杂的功能组件。方便使用。未来它将分为组件、样式、js三者合为一体,但分别提供。
本文所用代码皆来源于组件库中的yImgPro组件。详细代码可至github查看。地址: yunUI 。
npm地址:yun-ui-micro
欢迎大家star!最近有想法对组件库按照新思路进行重构,各位有什么急切需要或常见使用的组件也欢迎提出!一起共建!

场景如下:
动画排序组件展示

首先分析此需求。有两点:

  1. 拖动时排序
  2. 拖动后排序

单从性能上看,第二个是有优势的。但是从用户体验上看,无疑要选择第一种方案。

除非你的需求是“不能拖动排序”。你可以放心的选择第二种方案。第二种方案在笔者的功能库中也有组件:yImg。本文思路是第一种。

区别于之前写的第二种思路的文章,第一种思路对布局和样式影响很重的点在于:拖动。
拖动时排序意味着这个元素被拖动时不能在原来位置上有“保留”。这很关键,因为我们可以利用“保留”点对第二种方案进行改造使之像第一种效果看齐,但体验上仍有差距。

所以,笔者选择了“定位”position。所有的元素都是定位的,这样拖动时只需要更改transformz-index即可达到效果。记住这一点,这会带来bug,虽然很好解决。
我的wxml决定这样写:

<view class="container"><view class="item-wrap" style="height: {{ itemWrapHeight }}px;"><viewclass="item {{cur == index? 'cur':''}} {{curZ == index? 'zIndex':''}} {{itemTransition ? 'itemTransition':''}}"wx:for="{{list}}" wx:key="{{index}}" id="item{{index}}" data-key="{{item.key}}" data-index="{{index}}"style="transform: translate3d({{index === cur ? tranX : item.tranX}}px, {{index === cur ? tranY: item.tranY}}px, 0px);"bind:longpress="longPress" catch:touchmove="touchMove" catch:touchend="touchEnd"><view class="info" style="width: {{imgShape.side}}rpx; height: {{imgShape.side}}rpx; padding: 0 {{imgShape.pd}}rpx {{imgShape.pd}}rpx 0;"><image mode="aspectFill" src="{{item.data}}"></image><i class="iconfont icon-delete" wx:if="{{showMenuImg}}" bind:tap="onDelImage" data-index="{{item.key}}"></i></view></view><view class="item-sel selectphoto"style="transform: translate3d({{selSite.tranX}}px, {{selSite.tranY}}px, 0px); width: {{imgShape.side - imgShape.pd}}rpx; height: {{imgShape.side - imgShape.pd}}rpx;" hidden="{{!canSelPhoto}}" bind:tap="onChooseImage"><i class="iconfont icon-jiashang"></i></view></view>
</view>

这个组件被变量控制的样式比yImg组件多了许多,但结构上精简了不少。我们来分析下:
首先,因为定位,子元素脱离文档流,所以我们需要动态为item-wrap元素赋height
然后是子元素的宽高和padding也是动态的,没事,我们会在初始化时动态的获取它 —— 顺便添加一些我们需要的数据。

初始化发生在用户选择完图片以后。这个函数中干了几件事:

  1. 初始化数据。将数组-字符串变成数组-对象,存储初始顺序、未来可能的移动距离、图片本身
  2. 获取子项item的宽高(为偏移做准备)、计算当前整个区域高度(所以每次选择完图片都要调用此函数)
  3. 获取图片区域item-wrap的初始信息(位置!)
init() {// 遍历数据源增加扩展项, 以用作排序使用let list = this.data.listData.map((item, index) => {let data = {key: index,tranX: 0,tranY: 0,data: item}return data});this.setData({list: list,itemTransition: false});// 获取每一项的宽高等属性this.createSelectorQuery().select(".item").boundingClientRect((res) => {let rows;let len = this.data.list.length;if(len == MAX_IMG_NUM) {rows = Math.ceil(len / this.data.columns);}else {rows = Math.ceil((len + 1) / this.data.columns);}this.item = res;let itemWrapHeight = rows * res.height;this.getPosition(this.data.list, false);let obj = list[list.length - 1]let tranX = res.width * ((obj.key + 1) % this.data.columns);let tranY = Math.floor((obj.key + 1) / this.data.columns) * res.height;this.setData({itemWrapHeight,selSite: {tranX,tranY}})let query = wx.createSelectorQuery().in(this);query.select('.item-wrap').boundingClientRect((res) => {this.itemWrap = res;})// 需要的是“距离文档流顶部的距离”。所以咱们需要这片区域已经在页面上滚动了多少了,把这个值加上if(this.properties.scrollOffset) {this.itemWrap.top += this.properties.scrollOffset;}else {query.selectViewport().scrollOffset((res) => {let _wrap = this.itemWrap.top + res.scrollTop;this.itemWrap.top = _wrap;})}query.exec()}).exec();
},

这里尤其需要注意的是:获取图片区域信息时用的 API 只能获取“当前元素距离屏幕顶部的距离”。而实际大多数情况我们需要的是“当前元素距离文档流顶部的距离”。这两者在一个非常重要的场景下会有大幅偏差 —— 当文档流发生滚动时!
所以笔者采用selectViewport().scrollOffset API 来获得文档流的滚动偏差。并在后面长按甚至拖动过程中将这个偏差“抹去”。

如果你在使用笔者的组件库,并且遇到了“当前组件并不是一开始就在页面上出现而是动态展示”的场景,那么您也可以通过参数将这个“偏差”传入组件。这一点在README的使用说明中也有说明。

因为图片是 position 的,所以哪怕在初始时他们的位置也是计算得到的 —— getPosition 函数。我们根据列数获取每个元素的偏移距离,响应式到他们的transform上:

getPosition(data, vibrate = true) {let list = data.map((item, index) => {item.tranX = this.item.width * (item.key % this.data.columns);item.tranY = Math.floor(item.key / this.data.columns) * this.item.height;return item});this.setData({list: list});if (!vibrate) return;let listData = [];list.forEach((item) => {listData[item.key] = item.data});this.setData({listData,itemTransition: true})
},

接下来就是长按事件了。
在本组件中,少于两张图片则长按只有删除功能,一定程度上减少性能消耗。
长按时,我们需要拿到当前元素的位置,并且和整体区域位置结合获取“中心点”,并将中心点移动到点击位置处。这也就是我们说的“是否跟手”。并且这样图片的偏移也能提醒用户当前点的是这张图片:

longPress(e) {if(this.data.list.length < 2){this.setData({showMenuImg: true});wx.vibrateShort();return;}this.setData({touch: true});this.startX = e.changedTouches[0].pageXthis.startY = e.changedTouches[0].pageYlet index = e.currentTarget.dataset.index;this.tranX = this.startX - this.item.width / 2 - this.itemWrap.left;this.tranY = this.startY - this.item.height / 2 - this.itemWrap.top;this.setData({cur: index,curZ: index,tranX: this.tranX,tranY: this.tranY,showMenuImg: true});wx.vibrateShort();
},

长安之后是拖动。这是我们的核心事件。因为拖动排序,所以我们不仅需要计算当前元素的偏移,还需要计算元素偏移后和“路过元素”的位置关系 —— 临界点判断。

touchMove(e) {if (!this.data.touch) return;let tranX = e.touches[0].pageX - this.startX + this.tranX,tranY = e.touches[0].pageY - this.startY + this.tranY;this.setData({tranX: tranX,tranY: tranY,showMenuImg: false});let originKey = e.currentTarget.dataset.key;let endKey = this.calculateMoving(tranX, tranY);// 防止拖拽过程中发生乱序问题if (originKey == endKey || this.originKey == originKey) return;this.originKey = originKey;this.insert(originKey, endKey);
},

calculateMoving 函数就是做这个的:

calculateMoving(tranX, tranY) {let rows = Math.ceil(this.data.list.length / this.data.columns) - 1,i = Math.round(tranX / this.item.width),j = Math.round(tranY / this.item.height);i = i > (this.data.columns - 1) ? (this.data.columns - 1) : i;i = i < 0 ? 0 : i;j = j < 0 ? 0 : j;j = j > rows ? rows : j;let endKey = i + this.data.columns * j;endKey = endKey >= this.data.list.length ? this.data.list.length - 1 : endKey;return endKey
},

在拖动过程中,每次知道要偏移到哪,也就是抢占哪个元素的位置后,需要根据拖动元素的key 和 “目标元素”的 key 去重新计算每一项的新的key:

insert(origin, end) {let list;if (origin < end) {list = this.data.list.map((item) => {if (item.key > origin && item.key <= end) {item.key = item.key - 1;} else if (item.key == origin) {item.key = end;}return item});this.getPosition(list);} else if (origin > end) {list = this.data.list.map((item) => {if (item.key >= end && item.key < origin) {item.key = item.key + 1;} else if (item.key == origin) {item.key = end;}return item});this.getPosition(list);}
},

最后在“松手”时要去把所有在拖动过程中发生变化的变量给恢复初始值:

touchEnd() {if (!this.data.touch) {return;}else {this.setData({showMenuImg: true})}this.triggerMsg(this.data.listData, "sort-img")this.clearData();
},
clearData() {this.originKey = -1;this.setData({touch: false,cur: -1,tranX: 0,tranY: 0});// 延迟清空setTimeout(() => {this.setData({curZ: -1,})}, 300)
},

除此之外,还有删除事件。
删除也有两种方案:

  1. 硬删除。删除指定元素后将数组重新初始化init
  2. 有过渡效果的删除。从前到后计算删除元素位置,再从后到前将后一个元素的data赋值给前一个元素。但是其余key、tranX、tranY不变。最后len - 1

第二种方式相当于自己重新算了一遍。从一个地方可以看出两者的区别:需不需要自己计算“上传图片按钮的位置”!在笔者的组件中,也提供了参数可以选择使用哪种方式删除。
(如果放出代码的话整个篇幅就太多了,而且上面其实已经将代码的思路给说完了,“删除功能”的具体代码可以到github上看)

相关文章:

yunUI组件库解析:图片上传与排序组件yImgPro

yunUI是笔者开源的微信小程序功能库。目前其中包含了一些复杂的功能组件。方便使用。未来它将分为组件、样式、js三者合为一体&#xff0c;但分别提供。 本文所用代码皆来源于组件库中的yImgPro组件。详细代码可至github查看。地址&#xff1a; yunUI 。 npm地址&#xff1a;yu…...

Java基础:回调函数

因为在看Android代码的时候发现了许多关于回调函数的知识, 所以去了解了一下. 对于我来说不太好懂, 因为我觉得看的那些博文的讲法对我来说很绕, 所以我在理解了之后想写一篇关于回调函数的博文来给和我一样理解能力稍差的人一点帮助. 回调函数的作用其实就是将需要这个功能的调…...

Springboot多环境配置

此文章是根据黑马程序员课程所做的笔记课程视频 多环境开发 ​ 什么是多环境&#xff1f;其实就是说你的电脑上写的程序最终要放到别人的服务器上去运行。每个计算机环境不一样&#xff0c;这就是多环境。常见的多环境开发主要兼顾3种环境设置&#xff0c;开发环境——自己用的…...

Java Number Math 类,超详细整理,适合新手入门

目录 一、什么是Java Number类&#xff1f; 二、Java Number类提供了哪些基本的数字操作&#xff1f; 三、什么是包装类&#xff1f; 所有的包装类都是抽象类 Number 的子类。 四、什么是Java Math 类 Test类案例&#xff1a;&#xff08;Math.PI 表示一个圆的周长与直径…...

俯瞰·明统系列·落霞与孤鹜齐飞、南征与北伐并举

尽江南百万兵&#xff0c;腰间宝剑血尤腥。 引言 元至正二十七年&#xff08;1367年&#xff09;四月&#xff0c;吴王朱元璋命中书右丞相徐达为征虏大将军、平章常遇春为副将军&#xff0c;率军25万由淮入河、北进中原&#xff08;第一次北伐&#xff09;。北伐中发布告北方官…...

Nodejs环境搭建和配置

Nodejs环境的搭建和配置 1、下载 官网&#xff1a;http://nodejs.cn/download/&#xff0c;选择windows64位 msi文件 2、安装和配置环境 双击安装之后&#xff0c;配置环境变量&#xff1a; ①系统变量那边创建NODE_PATH变量&#xff0c;值为nodejs文件夹的node_modules文…...

MybatisPlus------条件构造器Wrapper以及QueryWrapper用法(七)

MybatisPlus------条件构造器Wapper&#xff08;七&#xff09; Wrapper:条件构造器抽象类&#xff0c;最顶端父类 AbstarctWrapper&#xff1a;用于查询条件封装&#xff0c;生成sql的where条件。 QueryWrapper&#xff1a;查询条件封装&#xff08;可以用于查询、删除&#x…...

NetSuite Intercompany Framework 101

今朝&#xff0c;谈一谈Intercompany Framework&#xff0c;这是一个彰显NetSuite市场野心的基础功能框架。从20.2开始逐渐浮出水面&#xff0c;虽然经过过往的几个版本&#xff0c;不断推出组成功能&#xff0c;但目前仍然未见其全貌。 作为顾问&#xff0c;你必须关注它&…...

限时活动|凭徽章领披萨大奖,玩转Moonbeam治理论坛

动动手指&#xff0c;无需每天打卡&#xff0c;用刷手机的零碎时间领一份Web3惊喜&#xff01; 本次挑战的目标是鼓励大家参与社区治理、熟悉论坛操作。有关参与方式和原因的信息在Twitter上共享&#xff1a;有兴趣可以和ThinkWildCrypto一起探索论坛以解锁其功能、了解最近和正…...

Golang中struct{}和struct{}{}的区别你知道吗?

首先说下Golang中的结构体&#xff0c;结构体是由一系列具有相同类型或不同类型的数据构成的数据集合&#xff0c;Golang中使用关键字struct来创建一个结构体&#xff0c;语法如下&#xff1a;typeStudentstruct { Name string }下面定义一个Student结构体&#xff0c;例如&am…...

网络安全-信息收集- 谷歌浏览器插件收集信息,谷歌hacking搜索语法-带你玩不一样的搜索引擎

网络安全-信息收集- 谷歌浏览器插件收集信息&#xff0c;谷歌hacking搜索语法-带你玩不一样的搜索引擎 前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 …...

基础篇—一文掌握css的边框属性

CSS 边框属性 CSS边框属性允许你指定一个元素边框的样式和颜色。 1、边框样式 边框样式属性指定要显示什么样的边界。 border-style属性用来定义边框的样式 2、边框宽度 您可以通过 border-width 属性为边框指定宽度。 为边框指定宽度有两种方法:可以指定长度值,比如 2px…...

05服务发现:引入etcd服务注册中心

在分布式微服务架构中,服务注册发现组件(通常称为服务注册中心)往往有着举足轻重的作用,它的性能与稳定可能会直接影响到整个服务的状态,比如Spring Cloud中的Eureka、Dubbo中的Zookeeper等等,接下来我们就gRPC微服务中最常见的服务注册中心etcd,来讲述下两者在具体是怎…...

Pdfium.Net SDK 4.78.2704 完美Crack/Ptach

不限制时&#xff0c;/不限PDF体积、、、、、// version: 4.78.2704 | file size: 52.7 Mb Pdfium .Net SDK C# PDF 库 从头开始或从一堆扫描图像创建 PDF 编辑、合并、拆分和操作 PDF&#xff0c;提取文本和图像 嵌入独立的 Winforms 或 WPF PDF 查看器 支持&#xff1a;.Net…...

再学C语言38:指针操作

C提供了6种基本的指针操作 示例代码&#xff1a; #include <stdio.h>int main(void) {int arr[5] {1, 2, 3, 4, 5};int * p1, *p2, *p3;p1 arr; // 把一个地址赋给指针p2 &arr[2]; // 把一个地址赋给指针printf("指针指向的地址&#xff0c;指针指向地址中…...

【论文Word排版】使用多级列表设置论文序号

在Word中对论文进行排版 1.设置章节前面的序号 1.1 需求 通常情况下要求如下 一级标题“第一章 XXX”&#xff0c;然后是“1.1 研究意义”&#xff0c; “1.2 研究现状” 之前的处理方式都是手打&#xff0c;并没有借助word的多级列表实现。这次趁着写毕业论文研究了一下。…...

分支管理方案

背景 在工作的过程中&#xff0c;git管理方式已经成为每一个项目开发的基础&#xff0c;每个项目的开发都离不开git管理方式。 但是在使用的过程中&#xff0c;由于对git分支管理方案的了解不深&#xff0c;导致会出现分支管理不明确的情况。 本文主要是做科普作用&#xff…...

Allegro走线时如何自动关闭其它网络飞线显示操作指导

Allegro走线时如何自动关闭其它网络飞线显示操作指导 在做PCB设计的时候,尤其是在评估布线的时候,走某一个网络的时候,希望其它网络的飞线会被自动关闭,方便评估。 Allegro支持这个功能,如下图 走线前 走线后 具体操作如下 点击Route...

Linux中常用命令汇总二

Linux中常用命令汇总一文章地址&#xff1a;https://blog.csdn.net/u011837804/article/details/1289952531、时间日期类基本语法date [OPTION]... [FORMAT]选项说明选项说明-d<时间字符串>显示指定的“时间字符串”表示的时间&#xff0c;而非当前时间-s<日期时间>…...

【数据结构】排序算法

目录 1.理解排序 1.1 排序的概念 1.2 排序的运用场景 1.3 常见的排序算法 2.插入排序算法 2.1 直接插入排序 2.2 希尔排序 3.选择排序算法 3.1 直接选择排序 3.2 堆排序 4.交换排序算法 4.1 冒泡排序 4.2 快速排序 4.2.1 hoare 法 4.2.2 挖坑法 4.2.3 前…...

[MySQL]初识数据库

哈喽&#xff0c;大家好&#xff01;我是保护小周ღ&#xff0c;本期为大家带来的是 MySQL 数据库&#xff0c;也是新的知识&#xff0c;首先我们会初步认识什么是数据库&#xff0c;什么是Mysql 数据库&#xff0c;以及我们 mysql 主要学什么&#xff0c;SQL 语句简单使用&…...

XXL-JOB分布式任务调度框架(二)-路由策略

文章目录1.引言2.任务详解2.1.执行器2.2.基础配置3.路由策略(第一个)-案例4.路由策略(最后一个)-案例5.轮询策略-案例7.分片广播任务1.引言 本篇文章承接上文《XXL-JOB分布式任务调度框架(一)-基础入门》&#xff0c;上一次和大家简单介绍了下 xxl-job 的由来以及使用方法&…...

Java_Maven:5. 把第三方 jar 包放入本地仓库或私服

目录 1 导入本地库 2 导入私服 3 参数说明 1 导入本地库 随便找一个 jar 包测试&#xff0c;可以先 CMD进入到 jar 包所在位置&#xff0c;运行 mvn install:install-file -DgroupIdcom.alibaba -DartifactIdfastjson -Dversion1.1.37-Dfile fastjson-1.1.37.jar -Dpackaging…...

【剑指offer】03~05. 数组中的数字(C# 实现)

文章目录前言03. 数组中重复的数字04. 二维数组中的查找05. 替换空格结语前言 &#x1f603; 大家好&#xff0c;我是writer桑&#xff0c;这是自己整理的 C# 做题记录&#xff0c;方便自己学习的同时分享出来&#xff0c;感谢支持。 03. 数组中重复的数字 题目描述&#xff1…...

Docker入门教程

文章目录一、Docker概述1. 什么是容器技术&#xff1f;2. 什么是Docker3. 为什么要使用Docker4. Docker和虚拟机的对比5. Docker相关概念6. DockerHub7. Docker架构二、安装Docker1. 安装Docker2. 配置阿里云镜像加速三、Docker常用命令1. 帮助命令2. 镜像操作命令3. 容器操作命…...

I2C总线应用测试程序

参考链接&#xff1a;I2c协议 Linux I2C应用编程开发 问题背景 在工作中需要测试I2C总线的传输稳定性&#xff0c;需写一个测试程序通过读写从设备寄存器的值来验证数据传输稳定性。 站在cpu的角度来看&#xff0c;操作I2C外设实际上就是通过控制cpu中挂载该I2C外设的I2C控制…...

主从表的建立

//表查--病害id--主从表public static DataSet QueryGetQlgjDispdbdisidTABbyqidZC(string qid, string bwname){string SQLStringZ "select * from tl_qlsoft_cql_qlcheck_qlstye_bw a, tl_qlsoft_cql_qlcheck_qlstye_bw_gj b where a.chbwidb.chbwid and a.qli…...

Exporter介绍与指标数据,规范说明(更新中)

1.exporter是什么广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target&#xff0c;如下所示&#xff0c;Prometheus通过轮询的方式定期从这些target中获取样本数据:2.exporter的来源与分类从Exporter的来源上来讲&am…...

[Android Studio] Android Studio Virtual Device虚拟机的功能试用

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f680;write…...

华为OD机试 - 检查数组中是否存在满足规则的数组组合1(Python)

检查数组中是否存在满足规则的数组组合1 题目 给定一个正整数数组 检查数组中是否存在满足规则的数组组合 规则: A = B + 2C 输入 第一行输出数组的元素个数 接下来一行输出所有数组元素,用空格隔开 输出 如果存在满足要求的数 在同一行里依次输出规则里A B C的取值,用…...

帮人做网站要怎么赚钱/浙江短视频seo优化网站

当前有效matplotlib版本为&#xff1a;3.4.1。 figure函数概述 在pyplot模块中&#xff0c;figure函数用于创建新的图形&#xff0c;或激活已存在的图形。 函数的签名为matplotlib.pyplot.figure(numNone, figsizeNone, dpiNone, facecolorNone, edgecolorNone, frameonTrue…...

网站基础功能介绍/做网站哪个公司最好

针对蒙蔽软件遮挡剔除的扩展&#xff0c;本文提供了一种灵活的解决方案&#xff0c;可以在不影响原始蒙蔽遮挡算法性能的情况下&#xff0c;为无法充分预分类的场景提高深度缓冲区的最终精度。该方法中的合并时间与分辨率呈线性关系&#xff0c;并且与场景中的几何复杂度无关。…...

专业的模板建站企业/seo推广公司排名

Bootstrap资源: 兼容性不错的简洁BuiBootstrap后台管理系统模板 [url]http://www.js-css.cn/divcss/admin/bui-bootstrap[/url], 下载 [url]http://www.js-css.cn/a/css/template/admin/2013/1108/1023.html[/url] 值得收藏&#xff01;4个Bootstrap3开发的后台管理模板 [url]h…...

想开一个网站开发的公司/班级优化大师怎么用

免费开通大数据服务&#xff1a;https://www.aliyun.com/product/odps “用户每天产生的日志量大约在2TB。我们需要将这些海量的数据导入云端&#xff0c;然后分天、分小时的展开数据分析作业&#xff0c;分析结果再导入数据库和报表系统&#xff0c;最终展示在运营人员面前。”…...

这几年做哪些网站致富/营销策划方案包括哪些内容

虽然现在jdk已经已经到了14【截至2020-08-15】&#xff0c;但是jdk8仍然是使用最广的版本&#xff0c;它的新特性不仅在我们日常开发中用到&#xff0c;在面试中也是一个经常被提问的点&#xff0c;所以对一些常用的还是需要知道的&#xff0c;我也只写一些常用的&#xff0c;有…...

怎么在濮阳网站做宣传/百度小说风云榜2022

第四章 计算机网络练习题1、Internet网采用的通信协议是( B )协议。A、x.25 B、TCP/IP C、SMIP D、IPX/SPX2、国际标准化组织的英文缩写是( B )。(A) OSI (B) ISO (C) ANSI (D) IEEE3、WWW采用( A )技术组织和管理浏览或信息检索的系统。A、超文本和超媒体 B、快速查询 C、动画…...