project init
This commit is contained in:
59
components/mark-slide-list/controller.js
Normal file
59
components/mark-slide-list/controller.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const defaultOptions = {
|
||||
only: true
|
||||
}
|
||||
|
||||
|
||||
export default function markSlideListController(options = defaultOptions) {
|
||||
const instances = {};
|
||||
const opened = {};
|
||||
const moving = {};
|
||||
let index = 0;
|
||||
const pipelines = {
|
||||
moving: [],
|
||||
opened: [],
|
||||
closed: [],
|
||||
};
|
||||
init()
|
||||
|
||||
function init() {
|
||||
if (options.only) {
|
||||
pipelines.moving.push(only)
|
||||
}
|
||||
}
|
||||
|
||||
function pipelinesHandler(type, key) {
|
||||
pipelines[type].forEach(item => {
|
||||
item.call(this, key)
|
||||
})
|
||||
}
|
||||
|
||||
function only() {
|
||||
for (let key in opened) {
|
||||
const item = opened[key]
|
||||
item.hide()
|
||||
}
|
||||
}
|
||||
this.reg = ({
|
||||
instance,
|
||||
cb
|
||||
}) => {
|
||||
instances[index] = instance
|
||||
cb(index++)
|
||||
}
|
||||
|
||||
this.moving = (key) => {
|
||||
moving[key] = instances[key]
|
||||
delete opened[key]
|
||||
pipelinesHandler('moving', key)
|
||||
}
|
||||
this.opened = (key) => {
|
||||
opened[key] = instances[key]
|
||||
delete moving[key]
|
||||
pipelinesHandler('opened', key)
|
||||
}
|
||||
this.closed = (key) => {
|
||||
delete opened[key]
|
||||
delete moving[key]
|
||||
pipelinesHandler('closed', key)
|
||||
}
|
||||
}
|
||||
186
components/mark-slide-list/mark-slide-list.vue
Normal file
186
components/mark-slide-list/mark-slide-list.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<view class="slide-list">
|
||||
<view class="slide-list-item" @touchstart="touchStart" @touchend="touchEnd" @touchmove="touchMove" :style="{transform}">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="group-btn">
|
||||
<view class="btn-div" v-for="(btn, key) in button" :key="key" @click.stop="btnClick(btn,key)" :style="{background: btn.background}">
|
||||
<text class="btn-title">{{btn.title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import handler from './slideHandler.js'
|
||||
/**
|
||||
* m-slide-list 滑动操作列表
|
||||
* @description 滑动操作列表组件
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=209
|
||||
* @property {Array} list 数据源,格式为:[{title: 'xxx', image:'xxx', surname: 'xxx',detail:'xxx', rightDetail: 'xxx', slide_x: 0},{title: 'xxx', image:'xxx', surname: 'xxx',detail:'xxx', rightDetail: 'xxx', slide_x: 0}]
|
||||
* @property {Array} button 按钮数据源,格式为:[{title: 'xxx', background:'xxx'},{title: 'xxx', background:'xxx'}]
|
||||
* @property {Boolean} border 边框
|
||||
*/
|
||||
export default {
|
||||
name: 'mark-slide-list',
|
||||
props: {
|
||||
button: { //按钮数据list
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
windowWidth() {
|
||||
return uni.getSystemInfoSync().windowWidth;
|
||||
},
|
||||
transform() {
|
||||
return `translate3d(${ this.slide_x }px, 0, 0)`
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
key: null,
|
||||
moving: false,
|
||||
btnState: 'hide',
|
||||
hideLimit: 0,
|
||||
showLimit: 0,
|
||||
btnWidth: 0,
|
||||
slide_x: 0,
|
||||
handler: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('controller-reg', {
|
||||
instance:{
|
||||
show: this.show,
|
||||
hide: this.hide
|
||||
},
|
||||
cb: (key) => {
|
||||
this.key = key
|
||||
}
|
||||
})
|
||||
const cb = res => {
|
||||
this.btnWidth = res[0].width * -1;
|
||||
this.showLimit = res[0].width / 3;
|
||||
this.hideLimit = res[0].width / 3;
|
||||
}
|
||||
// 按钮宽度
|
||||
uni.createSelectorQuery().in(this).select('.group-btn').boundingClientRect().exec(cb);
|
||||
},
|
||||
methods: {
|
||||
btnClick(btn, key) {
|
||||
this.$emit(btn.clickName, {
|
||||
btn,
|
||||
key
|
||||
})
|
||||
},
|
||||
setSlideX(offset) {
|
||||
const val = offset + this.slide_x
|
||||
if (val < 0 && val >= this.btnWidth) {
|
||||
this.slide_x = val
|
||||
} else if (val > 0) {
|
||||
this.slide_x = 0
|
||||
}
|
||||
},
|
||||
// 滑动开始
|
||||
touchStart(e) {
|
||||
this.$emit('controller-moving', this.key)
|
||||
this.moving = true
|
||||
this.handler = new handler(e.timeStamp, e.touches[0].pageX, e.touches[0].pageY)
|
||||
},
|
||||
// 滑动中
|
||||
touchMove(e) {
|
||||
const slide_x = this.handler.move(e.touches[0])
|
||||
this.setSlideX(slide_x)
|
||||
},
|
||||
// 滑动结束
|
||||
touchEnd(e) {
|
||||
this.moving = false
|
||||
const endTime = e.timeStamp,
|
||||
endY = e.changedTouches[0].pageY,
|
||||
endX = e.changedTouches[0].pageX;
|
||||
const x_end_distance = this.startX - this.lastX;
|
||||
const end = this.handler.endMove(endTime, endX, endY)
|
||||
if (end.invalid) {} else if ((end.quick && end.dir === 'l') || (end.dir === 'l' && end.move > this.showLimit)) {
|
||||
this.show()
|
||||
} else if ((end.quick && end.dir === 'r') || (end.dir === 'r' && end.move > this.hideLimit)) {
|
||||
this.hide()
|
||||
} else {
|
||||
this.recover()
|
||||
}
|
||||
this.handler = null
|
||||
},
|
||||
// 点击回复原状
|
||||
recover() {
|
||||
if (this.btnState === 'show') {
|
||||
this.show()
|
||||
} else if (this.btnState === 'hide') {
|
||||
this.hide()
|
||||
}
|
||||
},
|
||||
show() {
|
||||
this.btnState = 'show'
|
||||
this.slide_x = this.btnWidth;
|
||||
this.$emit('controller-opened', this.key)
|
||||
},
|
||||
hide() {
|
||||
this.btnState = 'hide'
|
||||
this.slide_x = 0;
|
||||
this.$emit('controller-closed', this.key)
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.slide-list {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.slide-list-item {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
transition: all 100ms;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.group-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
min-width: 100rpx;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.btn-div {
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
padding: 0 50rpx;
|
||||
font-size: 34rpx;
|
||||
display: table;
|
||||
|
||||
.btn-title {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
background-color: #c4c7cd;
|
||||
}
|
||||
|
||||
.removeM {
|
||||
background-color: #ff3b32;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
54
components/mark-slide-list/slideHandler.js
Normal file
54
components/mark-slide-list/slideHandler.js
Normal file
@@ -0,0 +1,54 @@
|
||||
class handler {
|
||||
|
||||
constructor(startTime, startX, startY) {
|
||||
this.startTime = startTime
|
||||
this.lastX = this.startX = startX
|
||||
this.startY = startY
|
||||
this._moveFunc = this.firstMove
|
||||
}
|
||||
|
||||
move({
|
||||
timeStamp,
|
||||
pageX,
|
||||
pageY
|
||||
}) {
|
||||
return this._moveFunc(timeStamp, pageX, pageY)
|
||||
}
|
||||
|
||||
|
||||
firstMove(timeStamp, pageX, pageY) {
|
||||
if (Math.abs(pageX - this.startX) > Math.abs(pageY - this.startY)) {
|
||||
this._moveFunc = this.nextMove
|
||||
} else {
|
||||
this._moveFunc = () => {
|
||||
return 0
|
||||
}
|
||||
this.endMove = () => {
|
||||
return {
|
||||
invalid: true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
nextMove(timeStamp, pageX, pageY) {
|
||||
const r = pageX - this.lastX
|
||||
this.lastX = pageX
|
||||
return r
|
||||
}
|
||||
|
||||
isQuick(endTime, endX) {
|
||||
return endTime - this.startTime < 200 && Math.abs(endX - this.startX) > 50
|
||||
}
|
||||
|
||||
endMove(endTime, pageX, pageY) {
|
||||
return {
|
||||
invalid: false,
|
||||
quick: this.isQuick(endTime, pageX),
|
||||
dir: pageX > this.startX ? 'r' : 'l',
|
||||
move: Math.abs(pageX - this.startX)
|
||||
}
|
||||
}
|
||||
}
|
||||
export default handler
|
||||
Reference in New Issue
Block a user