UNPKG

drip-ui

Version:

Lightweight Mobile UI Components built on Vue

439 lines (391 loc) 13.1 kB
import create from '../utils/create'; import draggable from './draggable'; import translateUtil from './translate'; import { once, addClass, removeClass } from './dom'; import Vue from 'vue'; import emitter from './emitter'; if (!Vue.prototype.$isServer) { require('raf.js'); } var rotateElement = function rotateElement(element, angle) { if (!element) return; var transformProperty = translateUtil.transformProperty; element.style[transformProperty] = element.style[transformProperty].replace(/rotateX\(.+?deg\)/gi, '') + (" rotateX(" + angle + "deg)"); }; var ITEM_HEIGHT = 36; var VISIBLE_ITEMS_ANGLE_MAP = { 3: -45, 5: -20, 7: -15 }; export default create({ render: function render() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "picker-slot", "class": _vm.classNames, style: _vm.flexStyle }, [_c('div', { staticClass: "picker-slot-mark_top", style: { 'height': _vm.markHeight } }), !_vm.divider ? _c('div', { ref: "wrapper", staticClass: "picker-slot-wrapper", "class": { dragging: _vm.dragging }, style: { height: _vm.contentHeight + 'PX' } }, _vm._l(_vm.mutatingValues, function (itemValue, index) { return _c('div', { key: index, staticClass: "picker-item", "class": { 'picker-selected': typeof itemValue === 'object' ? itemValue[_vm.valueKey] === _vm.currentValue[_vm.valueKey] : itemValue === _vm.currentValue }, style: { height: _vm.itemHeight + 'PX', lineHeight: _vm.itemHeight + 'PX' } }, [_vm._v("\n " + _vm._s(typeof itemValue === 'object' && itemValue[_vm.valueKey] ? itemValue[_vm.valueKey] : itemValue) + "\n ")]); })) : _vm._e(), _c('div', { staticClass: "picker-slot-mark_bottom", style: { 'height': _vm.markHeight } }), _vm.divider ? _c('div', [_vm._v(_vm._s(_vm.content))]) : _vm._e()]); }, name: 'picker-slot', props: { values: { type: Array, "default": function _default() { return []; } }, value: {}, visibleItemCount: { type: Number, "default": 5 }, // 当前数据展示内容的key valueKey: String, // 检测当前数据是否变化的key checkKey: { type: String, "default": 'value' }, rotateEffect: { type: Boolean, "default": false }, divider: { type: Boolean, "default": false }, textAlign: { type: String, "default": 'center' }, flex: {}, className: {}, content: {}, itemHeight: { type: Number, "default": ITEM_HEIGHT }, highlightHeight: { type: Number, "default": 50 }, defaultIndex: { type: Number, "default": 0, require: false } }, data: function data() { return { currentValue: this.value, mutatingValues: this.values, dragging: false, animationFrameId: null }; }, mixins: [emitter], computed: { flexStyle: function flexStyle() { return { 'flex': this.flex, '-webkit-box-flex': this.flex, '-moz-box-flex': this.flex, '-ms-flex': this.flex }; }, classNames: function classNames() { var PREFIX = 'picker-slot-'; var resultArray = []; if (this.rotateEffect) { resultArray.push(PREFIX + 'absolute'); } var textAlign = this.textAlign || 'center'; resultArray.push(PREFIX + textAlign); if (this.divider) { resultArray.push(PREFIX + 'divider'); } if (this.className) { resultArray.push(this.className); } return resultArray.join(' '); }, contentHeight: function contentHeight() { return this.itemHeight * (this.visibleItemCount - 1) + 50; }, markHeight: function markHeight() { return this.itemHeight * (this.visibleItemCount - 1) / 2 + "PX"; }, valueIndex: function valueIndex() { if (this.currentValue instanceof Object) { // 先判断当前数据是否含有传入的或者默认的 checkkey 值 // 有值根据 checkkey 检测数据变化,否则根据 valueKey 检测 var isHasCheckKey = this.currentValue.hasOwnProperty(this.checkKey); var checkKey = isHasCheckKey ? this.checkKey : this.valueKey; for (var i = 0, len = this.mutatingValues.length; i < len; i++) { if (this.currentValue[checkKey] === this.mutatingValues[i][checkKey]) { return i; } } return -1; } else { return this.mutatingValues.indexOf(this.currentValue); } }, dragRange: function dragRange() { var values = this.mutatingValues; var visibleItemCount = this.visibleItemCount; var itemHeight = this.itemHeight; return [-itemHeight * (values.length - Math.ceil(visibleItemCount / 2)), itemHeight * Math.floor(visibleItemCount / 2)]; }, minTranslateY: function minTranslateY() { return this.itemHeight * (Math.ceil(this.visibleItemCount / 2) - this.mutatingValues.length); }, maxTranslateY: function maxTranslateY() { return this.itemHeight * Math.floor(this.visibleItemCount / 2); } }, methods: { value2Translate: function value2Translate(value) { var _this = this; var values = this.mutatingValues; var valueIndex = -1; if (value instanceof Object) { values.forEach(function (item, index) { if (_this.isObjEqual(item, value)) { valueIndex = index; } }); } else { valueIndex = values.indexOf(value); } var offset = Math.floor(this.visibleItemCount / 2); var itemHeight = this.itemHeight; if (valueIndex !== -1) { return (valueIndex - offset) * -itemHeight; } }, isObjEqual: function isObjEqual(o1, o2) { var props1 = Object.keys(o1); var props2 = Object.keys(o2); if (props1.length !== props2.length) { return false; } for (var i = 0, max = props1.length; i < max; i++) { var propName = props1[i]; if (o1[propName] !== o2[propName]) { return false; } } return true; }, translate2Value: function translate2Value(translate) { var itemHeight = this.itemHeight; translate = Math.round(translate / itemHeight) * itemHeight; var index = -(translate - Math.floor(this.visibleItemCount / 2) * itemHeight) / itemHeight; return this.mutatingValues[index]; }, updateRotate: function updateRotate(currentTranslate, pickerItems) { var _this2 = this; if (this.divider) return; var dragRange = this.dragRange; var wrapper = this.$refs.wrapper; if (!pickerItems) { pickerItems = wrapper.querySelectorAll('.picker-item'); } if (currentTranslate === undefined) { currentTranslate = translateUtil.getElementTranslate(wrapper).top; } var itemsFit = Math.ceil(this.visibleItemCount / 2); var angleUnit = VISIBLE_ITEMS_ANGLE_MAP[this.visibleItemCount] || -20; [].forEach.call(pickerItems, function (item, index) { var itemOffsetTop = index * _this2.itemHeight; var translateOffset = dragRange[1] - currentTranslate; var itemOffset = itemOffsetTop - translateOffset; var percentage = itemOffset / _this2.itemHeight; var angle = angleUnit * percentage; if (angle > 180) angle = 180; if (angle < -180) angle = -180; rotateElement(item, angle); if (Math.abs(percentage) > itemsFit) { addClass(item, 'picker-item-far'); } else { removeClass(item, 'picker-item-far'); } }); }, planUpdateRotate: function planUpdateRotate() { var _this3 = this; var el = this.$refs.wrapper; cancelAnimationFrame(this.animationFrameId); this.animationFrameId = requestAnimationFrame(function () { _this3.updateRotate(); }); once(el, translateUtil.transitionEndProperty, function () { cancelAnimationFrame(_this3.animationFrameId); _this3.animationFrameId = null; }); }, initEvents: function initEvents() { var _this4 = this; var el = this.$refs.wrapper; var dragState = {}; var velocityTranslate, prevTranslate, pickerItems; draggable(el, { start: function start(event) { cancelAnimationFrame(_this4.animationFrameId); _this4.animationFrameId = null; dragState = { range: _this4.dragRange, start: new Date(), startLeft: event.pageX, startTop: event.pageY, startTranslateTop: translateUtil.getElementTranslate(el).top }; pickerItems = el.querySelectorAll('.picker-item'); }, drag: function drag(event) { _this4.dragging = true; dragState.left = event.pageX; dragState.top = event.pageY; var deltaY = dragState.top - dragState.startTop; var translate = dragState.startTranslateTop + deltaY; translateUtil.translateElement(el, null, translate); velocityTranslate = translate - prevTranslate || translate; prevTranslate = translate; if (_this4.rotateEffect) { _this4.updateRotate(prevTranslate, pickerItems); } }, end: function end(event) { _this4.dragging = false; var momentumRatio = 7; var currentTranslate = translateUtil.getElementTranslate(el).top; var duration = new Date() - dragState.start; var distance = Math.abs(dragState.startTranslateTop - currentTranslate); var itemHeight = _this4.itemHeight; var visibleItemCount = _this4.visibleItemCount; var rect, offset; if (distance < 6) { rect = _this4.$el.getBoundingClientRect(); offset = Math.floor((event.clientY - (rect.top + (visibleItemCount - 1) * itemHeight / 2)) / itemHeight) * itemHeight; if (offset > _this4.maxTranslateY) { offset = _this4.maxTranslateY; } velocityTranslate = 0; currentTranslate -= offset; } var momentumTranslate; if (duration < 300) { momentumTranslate = currentTranslate + velocityTranslate * momentumRatio; } var dragRange = dragState.range; _this4.$nextTick(function () { var translate; if (momentumTranslate) { translate = Math.round(momentumTranslate / itemHeight) * itemHeight; } else { translate = Math.round(currentTranslate / itemHeight) * itemHeight; } translate = Math.max(Math.min(translate, dragRange[1]), dragRange[0]); translateUtil.translateElement(el, null, translate); _this4.currentValue = _this4.translate2Value(translate); if (_this4.rotateEffect) { _this4.planUpdateRotate(); } }); dragState = {}; } }); }, doOnValueChange: function doOnValueChange() { var value = this.currentValue; var wrapper = this.$refs.wrapper; translateUtil.translateElement(wrapper, null, this.value2Translate(value)); }, doOnValuesChange: function doOnValuesChange() { var _this5 = this; var el = this.$el; var items = el.querySelectorAll('.picker-item'); [].forEach.call(items, function (item, index) { translateUtil.translateElement(item, null, _this5.itemHeight * index); }); if (this.rotateEffect) { this.planUpdateRotate(); } } }, mounted: function mounted() { this.ready = true; this.$emit('input', this.currentValue); if (!this.divider) { this.initEvents(); this.doOnValueChange(); } if (this.rotateEffect) { this.doOnValuesChange(); } }, watch: { values: function values(val) { this.mutatingValues = val; }, mutatingValues: function mutatingValues(val) { var _this6 = this; if (this.valueIndex === -1) { this.currentValue = (val || [])[0]; } if (this.rotateEffect) { this.$nextTick(function () { _this6.doOnValuesChange(); }); } }, currentValue: function currentValue(val) { this.doOnValueChange(); if (this.rotateEffect) { this.planUpdateRotate(); } this.$emit('input', val); this.dispatch('picker', 'slotValueChange', this); }, defaultIndex: function defaultIndex(val) { if (this.mutatingValues[val] !== undefined && this.mutatingValues.length >= val + 1) { this.currentValue = this.mutatingValues[val]; } } } });