├── App.vue ├── README.md ├── components └── cl-cardDel │ └── cl-cardDel.js ├── main.js ├── manifest.json ├── pages.json ├── pages ├── index │ └── index.vue ├── qzone │ └── index.vue └── tantan │ ├── card-box.vue │ └── index.vue ├── static ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg └── logo.png └── uni.scss /App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cardDel 2 | 3 | 卡片删除效果(仿探探) 4 | 5 | # 说明 6 | 7 | 插件设计: 插件不仅仅可以实现仿探探的效果,还能通过参数实现卡片不同的动画效果的变化。 8 | 9 | 基本模板里提供了简单的动画效果。 10 | 11 | 12 | ## 使用方式 13 | 14 | **在index.js中** 15 | 16 | ~~~ 17 | import clCardDel from "@/components/cl-cardDel/cl-cardDel"; 18 | mixins:[clCardDel] 19 | ~~~ 20 | 21 | ## OBJECT参数说明 22 | 23 | | 参数 | 类型 | 默认值 | 说明 | 24 | | --- | --- | --- | --- | 25 | | number | Number | 2 | 存在的卡片数量 | 26 | | moveRotate | Object | { x:0,y:0 } | 设置位移图片旋转角度距离 card中心点 - 指向坐标 | 27 | | delMoveD | Number | screenHeight*2 | 删除移动距离 | 28 | | touchMoveD | Number | 100 | card移动距离 card移动距离/touchMoveD = 其他card变化比率 | 29 | | rotate | Number | 0 | 第2张卡片旋转角度 | 30 | | scale | Object | { x:1,y:1 } | 第2张卡片缩放 | 31 | | skew | Object | { x:0,y:0 } | 第2张卡片倾斜 | 32 | | translate | Object | { x:0,y:0 } | 第2张卡片位移 | 33 | | opacity | Number | 1 | 第2张卡片透明度 | 34 | 35 | ## 事件 36 | 37 | | 事件名 | 说明 | 38 | | --- | --- | 39 | | init | 设置初始参数 | 40 | | getData | 获取数据 | 41 | | moveJudge(x,y,ratio) | 触摸中判断 | 42 | | endJudge(x,y) | 触摸结束判断 | 43 | | _back | 执行回退动画 | 44 | | _del | 执行删除动画 | 45 | 46 | ## 如果觉得插件不错,麻烦给个好评 47 | -------------------------------------------------------------------------------- /components/cl-cardDel/cl-cardDel.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data(){ 3 | return{ 4 | number:2, //展示卡片数量,同时设置animationData对象 5 | moveRotate:{ x:0,y:0 }, //设置位移图片旋转角度距离 card中心点 - 指向坐标 6 | delMoveD: uni.getSystemInfoSync().screenHeight,//设置删除移动距离 7 | touchMoveD: 100,//设置card移动距离, card移动距离/touchMoveD = 其他card变化比率 8 | rotate:0, //旋转deg 设置第2张卡片transform opacity 9 | scale:{ x:1,y:1 }, //缩放 10 | skew:{ x:0,y:0 }, //倾斜px 11 | translate:{ x:0,y:0 }, //位移px 12 | opacity:1, //透明度,参数范围 0~1 13 | type:false, //是否拥有两套代码 14 | 15 | 16 | moveX:0, //记录移动值 17 | moveY:0, // 18 | oldTouces:{}, 19 | oldMove:{}, 20 | touchAnimation:null, 21 | animationData:{}, 22 | dataList:[], 23 | delTime:150, 24 | cardId:0, 25 | x:0, 26 | y:0, 27 | sysHeight:0, 28 | sysWidth:0, 29 | delFlag:false, //app下禁止快速滑动150ms 30 | } 31 | }, 32 | created() { 33 | this.init() 34 | this.sysHeight = uni.getSystemInfoSync().windowHeight 35 | this.sysWidth = uni.getSystemInfoSync().windowWidth 36 | }, 37 | async mounted() { 38 | 39 | this.createAnimation() 40 | await this.getData() 41 | }, 42 | methods:{ 43 | //初始值设置 44 | init(){ 45 | 46 | }, 47 | //获取数据 48 | getData(){ 49 | 50 | }, 51 | createAnimation(){ 52 | //touch移动动画 53 | this.touchAnimation = uni.createAnimation({ 54 | duration:0 55 | }); 56 | //其他卡片移动 57 | this.moveAnimation = uni.createAnimation({ 58 | duration:0 59 | }); 60 | //删除动画 61 | this.delanimation = uni.createAnimation({ 62 | duration:this.delTime, 63 | 64 | }); 65 | //删除时其他card动画 66 | this.endanimation = uni.createAnimation({ 67 | duration:200 68 | }); 69 | }, 70 | touchStart(e){ 71 | 72 | this.oldTouces = e.touches[0] 73 | 74 | }, 75 | touchMove(e) { 76 | 77 | if(this.delFlag) return 78 | 79 | 80 | let { oldTouces } = this 81 | 82 | 83 | let newTouces = e.touches[0] 84 | //平移 85 | this.moveX = newTouces.clientX-oldTouces.clientX 86 | this.moveY = newTouces.clientY-oldTouces.clientY 87 | this.dataList[0].moveX = this.moveX 88 | this.dataList[0].moveY = this.moveY 89 | 90 | this.dataList[0].animation = false 91 | //移动图片旋转角度 92 | let angle = this.calcAngleDegrees(this.moveX- this.moveRotate.x,this.moveY- this.moveRotate.y ) 93 | 94 | //移动card动画 95 | if(this.type){ 96 | //#ifdef APP-PLUS 97 | this.touchAnimation.translateX(this.moveX).translateY(this.moveY).rotate(angle).step(); 98 | //#endif 99 | //#ifndef APP-PLUS 100 | this.touchAnimation.rotate(angle).step(); 101 | //#endif 102 | }else{ 103 | this.touchAnimation.rotate(angle).step(); 104 | } 105 | 106 | 107 | 108 | 109 | 110 | this.animationData[0] = this.touchAnimation.export() 111 | 112 | //其他card动画 113 | let d = this.moveX*this.moveX + this.moveY*this.moveY 114 | let ratio = Math.sqrt(d) / this.touchMoveD 115 | ratio = ratio > 1 ? 1 : ratio 116 | 117 | for (var i = 1; i < this.number; i++) { 118 | 119 | if(this.rotate!=0) this.moveAnimation.rotate( this.rotate*(i-ratio) ) 120 | if(this.opacity!=1) this.moveAnimation.opacity( 1-(1-this.opacity)*(i-ratio) ) 121 | if(this.scale.x!=1) this.moveAnimation.scaleX( 1-(1-this.scale.x)*(i-ratio) ) 122 | if(this.scale.y!=1) this.moveAnimation.scaleY( 1-(1-this.scale.y)*(i-ratio) ) 123 | if(this.skew.x!=0) this.moveAnimation.skewX( this.skew.x*(i-ratio) ) 124 | if(this.skew.y!=0) this.moveAnimation.skewY( this.skew.y*(i-ratio) ) 125 | if(this.translate.x!=0) this.moveAnimation.translateX( this.translate.x*(i-ratio) ) 126 | if(this.translate.y!=0) this.moveAnimation.translateY( this.translate.y*(i-ratio) ) 127 | 128 | this.moveAnimation.step() 129 | this.animationData[i] = this.moveAnimation.export() 130 | } 131 | //触摸中视图变化 132 | this.moveJudge(this.moveX,this.moveY,ratio) 133 | }, 134 | touchend(e){ 135 | 136 | this.endJudge(this.moveX,this.moveY) 137 | 138 | }, 139 | //触摸中判断 140 | moveJudge(x,y,ratio){ 141 | 142 | }, 143 | //触摸结束判断 144 | endJudge(x,y){ 145 | if(x!=0||y!=0){ 146 | this._del() 147 | } 148 | }, 149 | //返回card 150 | _back(){ 151 | 152 | let { oldMove } = this 153 | //移动图片旋转角度 154 | let angle = this.calcAngleDegrees(this.moveX- this.moveRotate.x,this.moveY- this.moveRotate.y ) 155 | //移动card动画 156 | 157 | if(this.type){ 158 | //#ifdef APP-PLUS 159 | this.delanimation.translateX(-this.moveX/3).translateY(-this.moveY/3).rotate(0).step(); 160 | //#endif 161 | 162 | }else{ 163 | 164 | } 165 | 166 | 167 | this.moveX = 0 168 | this.moveY = 0 169 | this.dataList[0].moveX = 0 170 | this.dataList[0].moveY = 0 171 | this.dataList[0].animation = true 172 | this.delanimation.translateX(this.moveX).translateY(this.moveY).rotate(0).step(); 173 | this.animationData[0] = this.delanimation.export() 174 | setTimeout(() => { 175 | //清除动画 176 | this.animationData[0] = this.delanimation.export() 177 | for (var i = 1; i < this.number; i++) { 178 | this.animationData[i] = this.moveAnimation.export() 179 | } 180 | this.moveX = 0 181 | this.moveY = 0 182 | }, this.delTime) 183 | 184 | //其他card动画 185 | let ratio = 0 186 | 187 | for (var i = 1; i < this.number; i++) { 188 | 189 | if(this.rotate!=0) this.endanimation.rotate( this.rotate*(i-ratio) ) 190 | if(this.opacity!=1) this.endanimation.opacity( 1-(1-this.opacity)*(i-ratio) ) 191 | if(this.scale.x!=1) this.endanimation.scaleX( 1-(1-this.scale.x)*(i-ratio) ) 192 | if(this.scale.y!=1) this.endanimation.scaleY( 1-(1-this.scale.y)*(i-ratio) ) 193 | if(this.skew.x!=0) this.endanimation.skewX( this.skew.x*(i-ratio) ) 194 | if(this.skew.y!=0) this.endanimation.skewY( this.skew.y*(i-ratio) ) 195 | if(this.translate.x!=0) this.endanimation.translateX( this.translate.x*(i-ratio) ) 196 | if(this.translate.y!=0) this.endanimation.translateY( this.translate.y*(i-ratio) ) 197 | 198 | this.endanimation.step() 199 | this.animationData[i] = this.endanimation.export() 200 | } 201 | }, 202 | //删除card 203 | _del(){ 204 | if(this.type) { 205 | //#ifdef APP-PLUS 206 | this.delFlag = true 207 | //#endif 208 | } 209 | 210 | 211 | //移动card动画 212 | let d = this.moveX*this.moveX + this.moveY*this.moveY 213 | let y = this.moveY*this.delMoveD/Math.sqrt(d) 214 | let x = this.moveX*this.delMoveD/Math.sqrt(d) 215 | this.delanimation.translateX(x).translateY(y).step(); 216 | this.animationData[0] = this.delanimation.export() 217 | setTimeout(() => { 218 | 219 | //清除动画 220 | this.animationData[0] = this.delanimation.export() 221 | for (var i = 1; i < this.number; i++) { 222 | this.animationData[i] = this.moveAnimation.export() 223 | } 224 | 225 | this.delCard(this.moveX,this.moveY) 226 | this.moveX = 0 227 | this.moveY = 0 228 | this.dataList[0].moveX = 0 229 | this.dataList[0].moveY = 0 230 | this.dataList.splice(0,1) 231 | if(this.dataList.length<=this.number) this.getData() 232 | if(this.type) { 233 | //#ifdef APP-PLUS 234 | this.delFlag = false 235 | //#endif 236 | } 237 | }, this.delTime) 238 | 239 | //其他card动画 240 | let ratio = 1 241 | 242 | for (var i = 1; i < this.number; i++) { 243 | 244 | if(this.rotate!=0) this.endanimation.rotate( this.rotate*(i-ratio) ) 245 | if(this.opacity!=1) this.endanimation.opacity( 1-(1-this.opacity)*(i-ratio) ) 246 | if(this.scale.x!=1) this.endanimation.scaleX( 1-(1-this.scale.x)*(i-ratio) ) 247 | if(this.scale.y!=1) this.endanimation.scaleY( 1-(1-this.scale.y)*(i-ratio) ) 248 | if(this.skew.x!=0) this.endanimation.skewX( this.skew.x*(i-ratio) ) 249 | if(this.skew.y!=0) this.endanimation.skewY( this.skew.y*(i-ratio) ) 250 | if(this.translate.x!=0) this.endanimation.translateX( this.translate.x*(i-ratio) ) 251 | if(this.translate.y!=0) this.endanimation.translateY( this.translate.y*(i-ratio) ) 252 | 253 | this.endanimation.step() 254 | this.animationData[i] = this.endanimation.export() 255 | } 256 | }, 257 | delCard(){ 258 | console.log(this.dataList[0]) 259 | }, 260 | calcAngleDegrees(x, y) { 261 | return Math.atan2(y, x) * 180 / Math.PI + 90; 262 | } 263 | }, 264 | watch:{ 265 | 266 | 267 | 268 | }, 269 | watch:{ 270 | number:{ 271 | immediate:true, 272 | handler(newVal,oldVal){ 273 | for (var i = 0; i < newVal; i++) { 274 | let a = {} 275 | a[i] = {} 276 | this.animationData = {...this.animationData,...a} 277 | } 278 | } 279 | }, 280 | dataList:{ 281 | handler(newVal,oldVal){ 282 | for (let item of newVal) { 283 | if(!item._id){ 284 | item._id = this.cardId++ 285 | item.moveX = 0 286 | item.moveY = 0 287 | item.animation = false 288 | } 289 | } 290 | 291 | } 292 | } 293 | } 294 | } 295 | 296 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | 4 | Vue.config.productionTip = false 5 | 6 | App.mpType = 'app' 7 | 8 | const app = new Vue({ 9 | ...App 10 | }) 11 | app.$mount() 12 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cardDel", 3 | "appid" : "", 4 | "description" : "", 5 | "versionName" : "1.0.0", 6 | "versionCode" : "100", 7 | "transformPx" : false, 8 | /* 5+App特有相关 */ 9 | "app-plus" : { 10 | "usingComponents" : true, 11 | "nvueCompiler" : "uni-app", 12 | "splashscreen" : { 13 | "alwaysShowBeforeRender" : true, 14 | "waiting" : true, 15 | "autoclose" : true, 16 | "delay" : 0 17 | }, 18 | /* 模块配置 */ 19 | "modules" : {}, 20 | /* 应用发布信息 */ 21 | "distribute" : { 22 | /* android打包配置 */ 23 | "android" : { 24 | "permissions" : [ 25 | "", 26 | "", 27 | "", 28 | "", 29 | "", 30 | "", 31 | "", 32 | "", 33 | "", 34 | "", 35 | "", 36 | "", 37 | "", 38 | "", 39 | "", 40 | "", 41 | "", 42 | "", 43 | "", 44 | "", 45 | "", 46 | "" 47 | ] 48 | }, 49 | /* ios打包配置 */ 50 | "ios" : {}, 51 | /* SDK配置 */ 52 | "sdkConfigs" : {} 53 | } 54 | }, 55 | /* 快应用特有相关 */ 56 | "quickapp" : {}, 57 | /* 小程序特有相关 */ 58 | "mp-weixin" : { 59 | "appid" : "", 60 | "setting" : { 61 | "urlCheck" : false 62 | }, 63 | "usingComponents" : true 64 | }, 65 | "mp-alipay" : { 66 | "usingComponents" : true 67 | }, 68 | "mp-baidu" : { 69 | "usingComponents" : true 70 | }, 71 | "mp-toutiao" : { 72 | "usingComponents" : true 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages 3 | { 4 | "path": "pages/index/index", 5 | "style": { 6 | "navigationBarTitleText": "基本模板" 7 | } 8 | }, 9 | { 10 | "path": "pages/qzone/index", 11 | "style": { 12 | "navigationBarTitleText": "qq空间" 13 | } 14 | }, 15 | { 16 | "path": "pages/tantan/index", 17 | "style": { 18 | "navigationBarTitleText": "探探" 19 | 20 | } 21 | } 22 | ], 23 | "globalStyle": { 24 | "navigationBarTextStyle": "black", 25 | "navigationBarTitleText": "uni-app", 26 | "navigationBarBackgroundColor": "#F8F8F8", 27 | "backgroundColor": "#F8F8F8" 28 | }, 29 | "tabBar": { 30 | "color": "#333333", 31 | "backgroundColor": "#F8F8F8", 32 | "selectedColor": "#00B26A", 33 | "list": [ 34 | { 35 | "pagePath": "pages/index/index", 36 | "text": "基本模板" 37 | }, 38 | { 39 | "pagePath": "pages/qzone/index", 40 | "text": "qq空间" 41 | }, 42 | { 43 | "pagePath": "pages/tantan/index", 44 | "text": "探探" 45 | } 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 76 | 77 | 115 | -------------------------------------------------------------------------------- /pages/qzone/index.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 91 | 92 | 163 | -------------------------------------------------------------------------------- /pages/tantan/card-box.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 133 | 134 | 323 | -------------------------------------------------------------------------------- /pages/tantan/index.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 173 | 174 | 175 | 247 | -------------------------------------------------------------------------------- /static/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/1.jpg -------------------------------------------------------------------------------- /static/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/2.jpg -------------------------------------------------------------------------------- /static/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/3.jpg -------------------------------------------------------------------------------- /static/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/4.jpg -------------------------------------------------------------------------------- /static/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/5.jpg -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15157757001/cardDel/a54c5aa01e87f150e82cf2aab6ba6563858db3a9/static/logo.png -------------------------------------------------------------------------------- /uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color:#333;//基本色 25 | $uni-text-color-inverse:#fff;//反色 26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable:#c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color:#ffffff; 32 | $uni-bg-color-grey:#f8f8f8; 33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color:#c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm:24upx; 43 | $uni-font-size-base:28upx; 44 | $uni-font-size-lg:32upx; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm:40upx; 48 | $uni-img-size-base:52upx; 49 | $uni-img-size-lg:80upx; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 4upx; 53 | $uni-border-radius-base: 6upx; 54 | $uni-border-radius-lg: 12upx; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 10px; 59 | $uni-spacing-row-base: 20upx; 60 | $uni-spacing-row-lg: 30upx; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 8upx; 64 | $uni-spacing-col-base: 16upx; 65 | $uni-spacing-col-lg: 24upx; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2C405A; // 文章标题颜色 72 | $uni-font-size-title:40upx; 73 | $uni-color-subtitle: #555555; // 二级标题颜色 74 | $uni-font-size-subtitle:36upx; 75 | $uni-color-paragraph: #3F536E; // 文章段落颜色 76 | $uni-font-size-paragraph:30upx; --------------------------------------------------------------------------------