diff --git a/docs/examples/picker.md b/docs/examples/picker.md index d9c84223b..f4d8959f3 100644 --- a/docs/examples/picker.md +++ b/docs/examples/picker.md @@ -1,7 +1,29 @@ + + ## Picker组件 模仿iOS中的`UIPickerView`。 +### 基础用法 + +:::demo 基础用法 +```html + +``` +::: + ### API | 参数 | 说明 | 类型 | 默认值 | 可选值 | diff --git a/package.json b/package.json index 2090aa0d9..f10b62000 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,6 @@ "rimraf": "^2.5.4", "run-sequence": "^1.2.2", "saladcss-bem": "^0.0.1", - "sass-loader": "^3.2.3", "style-loader": "^0.13.1", "theaterjs": "^3.0.0", "transliteration": "^1.1.11", diff --git a/packages/picker/src/draggable.js b/packages/picker/src/draggable.js new file mode 100644 index 000000000..4f524c986 --- /dev/null +++ b/packages/picker/src/draggable.js @@ -0,0 +1,51 @@ +import Vue from 'vue'; + +let isDragging = false; + +const supportTouch = !Vue.prototype.$isServer && 'ontouchstart' in window; + +export default function(element, options) { + const moveFn = function(event) { + if (options.drag) { + options.drag(supportTouch ? event.changedTouches[0] || event.touches[0] : event); + } + }; + + const endFn = function(event) { + if (!supportTouch) { + document.removeEventListener('mousemove', moveFn); + document.removeEventListener('mouseup', endFn); + } + document.onselectstart = null; + document.ondragstart = null; + + isDragging = false; + + if (options.end) { + options.end(supportTouch ? event.changedTouches[0] || event.touches[0] : event); + } + }; + + element.addEventListener(supportTouch ? 'touchstart' : 'mousedown', function(event) { + if (isDragging) return; + document.onselectstart = function() { return false; }; + document.ondragstart = function() { return false; }; + + if (!supportTouch) { + document.addEventListener('mousemove', moveFn); + document.addEventListener('mouseup', endFn); + } + isDragging = true; + + if (options.start) { + event.preventDefault(); + options.start(supportTouch ? event.changedTouches[0] || event.touches[0] : event); + } + }); + + if (supportTouch) { + element.addEventListener('touchmove', moveFn); + element.addEventListener('touchend', endFn); + element.addEventListener('touchcancel', endFn); + } +}; diff --git a/packages/picker/src/picker-column.vue b/packages/picker/src/picker-column.vue index b07316837..f0ab04053 100644 --- a/packages/picker/src/picker-column.vue +++ b/packages/picker/src/picker-column.vue @@ -1,14 +1,21 @@ - - - + + + {{item}} diff --git a/packages/picker/src/picker.vue b/packages/picker/src/picker.vue index 513e33c8b..90e2d6d7a 100644 --- a/packages/picker/src/picker.vue +++ b/packages/picker/src/picker.vue @@ -1,10 +1,10 @@ - + - + { - values.push(column.value || column[column.defaultIndex || 0]); + values.push(column.value || column.values[column.defaultIndex || 0]); }); return values; diff --git a/packages/zanui-css/src/index.pcss b/packages/zanui-css/src/index.pcss index 8a849223c..afe503ef7 100644 --- a/packages/zanui-css/src/index.pcss +++ b/packages/zanui-css/src/index.pcss @@ -8,4 +8,5 @@ @import './field.pcss'; @import './icon.pcss'; @import './popup.pcss'; +@import './picker.pcss'; @import './switch.pcss'; diff --git a/packages/zanui-css/src/picker.pcss b/packages/zanui-css/src/picker.pcss new file mode 100644 index 000000000..59947341d --- /dev/null +++ b/packages/zanui-css/src/picker.pcss @@ -0,0 +1,73 @@ +@component-namespace z { + @b picker { + overflow: hidden; + + @e toolbar { + height: 40px; + } + + @e columns { + position: relative; + overflow: hidden; + + @m 1 { + .z-picker-column { + width: 100%; + } + } + + @m 2 { + .z-picker-column { + width: 50%; + } + } + + @m 2 { + .z-picker-column { + width: 33.333%; + } + } + } + } + + @b picker-column { + font-size: 18px; + overflow: hidden; + position: relative; + max-height: 100%; + text-align: center; + + @e item { + height: 44px; + line-height: 44px; + padding: 0 10px; + white-space: nowrap; + position: relative; + overflow: hidden; + text-overflow: ellipsis; + color: #707274; + left: 0; + top: 0; + width: 100%; + box-sizing: border-box; + transition-duration: .3s; + backface-visibility: hidden; + + @m selected { + color: #000; + transform: translate3d(0, 0, 0) rotateX(0); + } + } + } + + .picker-column-wrapper { + transition-duration: 0.3s; + transition-timing-function: ease-out; + backface-visibility: hidden; + } + + .picker-column-wrapper.dragging, + .picker-column-wrapper.dragging .picker-item { + transition-duration: 0s; + } +} diff --git a/src/utils/transition.js b/src/utils/transition.js new file mode 100644 index 000000000..215dc0d4f --- /dev/null +++ b/src/utils/transition.js @@ -0,0 +1,100 @@ +import Vue from 'vue'; + +var exportObj = {}; + +if (!Vue.prototype.$isServer) { + var docStyle = document.documentElement.style; + var engine; + var translate3d = false; + + if (window.opera && Object.prototype.toString.call(opera) === '[object Opera]') { + engine = 'presto'; + } else if ('MozAppearance' in docStyle) { + engine = 'gecko'; + } else if ('WebkitAppearance' in docStyle) { + engine = 'webkit'; + } else if (typeof navigator.cpuClass === 'string') { + engine = 'trident'; + } + + var cssPrefix = {trident: '-ms-', gecko: '-moz-', webkit: '-webkit-', presto: '-o-'}[engine]; + + var vendorPrefix = {trident: 'ms', gecko: 'Moz', webkit: 'Webkit', presto: 'O'}[engine]; + + var helperElem = document.createElement('div'); + var perspectiveProperty = vendorPrefix + 'Perspective'; + var transformProperty = vendorPrefix + 'Transform'; + var transformStyleName = cssPrefix + 'transform'; + var transitionProperty = vendorPrefix + 'Transition'; + var transitionStyleName = cssPrefix + 'transition'; + var transitionEndProperty = vendorPrefix.toLowerCase() + 'TransitionEnd'; + + if (helperElem.style[perspectiveProperty] !== undefined) { + translate3d = true; + } + + var getTranslate = function(element) { + var result = {left: 0, top: 0}; + if (element === null || element.style === null) return result; + + var transform = element.style[transformProperty]; + var matches = /translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*translateZ\(0px\)/ig.exec(transform); + if (matches) { + result.left = +matches[1]; + result.top = +matches[3]; + } + + return result; + }; + + var translateElement = function(element, x, y) { + if (x === null && y === null) return; + + if (element === null || element === undefined || element.style === null) return; + + if (!element.style[transformProperty] && x === 0 && y === 0) return; + + if (x === null || y === null) { + var translate = getTranslate(element); + if (x === null) { + x = translate.left; + } + if (y === null) { + y = translate.top; + } + } + + cancelTranslateElement(element); + + if (translate3d) { + element.style[transformProperty] += ' translate(' + (x ? (x + 'px') : '0px') + ',' + (y ? (y + 'px') : '0px') + ') translateZ(0px)'; + } else { + element.style[transformProperty] += ' translate(' + (x ? (x + 'px') : '0px') + ',' + (y ? (y + 'px') : '0px') + ')'; + } + }; + + var cancelTranslateElement = function(element) { + if (element === null || element.style === null) return; + + var transformValue = element.style[transformProperty]; + + if (transformValue) { + transformValue = transformValue.replace(/translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*translateZ\(0px\)/g, ''); + element.style[transformProperty] = transformValue; + } + }; + + exportObj = { + transformProperty: transformProperty, + transformStyleName: transformStyleName, + transitionProperty: transitionProperty, + transitionStyleName: transitionStyleName, + transitionEndProperty: transitionEndProperty, + getElementTranslate: getTranslate, + translateElement: translateElement, + cancelTranslateElement: cancelTranslateElement + }; +}; + +export default exportObj; +