Vue realizes iOS native picker effect (Implementation thinking analysis)

Source: Internet
Author: User

Before the earliest implementation of a similar time-selection plug-in, but the scope of application is too narrow, simply to publish this implementation recently, rewrite a high-reuse Vue component.

Support Android 4.0 or above, Safari 7 or above

Effect Preview

GitHub

Wheel Part main DOM structure
<Template><Divclass="Pd-select-item" ><Divclass="Pd-select-line" ></Div><Ulclass= "pd-select-list" > <li class= "Pd-select-list-item" >1</li> </ul> <ul class= "Pd-select-wheel" > <li class=" Pd-select-wheel-item ">1</li>  </ul> </div> </TEMPLATE>          
Props
 props: {      data: {        type: Array,        true      },      type: {        type: String,        default: ‘cycle‘      },      value: {}    }
Set the CSS style to center vertically
.pd-select-line, .pd-select-list, .pd-select-wheel {    position: absolute;    left: 0; right: 0; top: 50%; transform: translateY(-50%);}.pd-select-list { overflow: hidden;}
Wheel 3d style settings
/* 滚轮盒子 */.pd-select-wheel {    transform-style: preserve-3d;    height: 30px;}/* 滚轮单项 */.pd-select-wheel-item { white-space: nowrap; text-overflow: ellipsis; backface-visibility: hidden; position: absolute; top: 0px; width: 100%; overflow: hidden;}

Main Note 2 Properties transform-style: preserve-3d; backface-visibility: hidden;
The first one is the 3d layout, the interface 3D, the second is to let the wheel behind the auto-hide (red part, the back of the DOM node will be automatically hidden)

How to implement a 3D roller

Box main this CSStransform: rotate3d(1, 0, 0, x deg);
Item mainly uses this CSStransform: rotate3d(1, 0, 0, xdeg) translate3d(0px, 0px, [x]px);


The above 2 pictures show the translate3d(0px, 0px, [x]px); effect of this sentence [x] is the radius of the circle

As you can see from the above figure, we just need to rotate each DOM itself, and then translate3d(0px, 0px, [x]px); expand each DOM by expanding
The ring is formed. α is the angle at which each DOM rotates itself, because it only uses 0 to 180 °, so a box is used to mount the DOM.

Row heights and angular calculations


Known both sides and angle calculate the third side length ~=34px
Http://tool.520101.com/calcul ...

Infinite Wheel implementation
/* 滚轮展示大小限定 */spin: {start: 0, end: 9, branch: 9}/* 获取spin 数据 */ getSpinData (index) { index = index % this.listData.length return this.listData[index >= 0 ? index : index + this.listData.length] } /* 模运算 获取数组有的索引 这样就构成 圆环了 */
Touchend Do special treatment

Set the SETCSS type in the touchend to take the scrolling data, so that when the stop is a lattice of accurate rotation in place

// other code .... /* 计算touchEnd移动的整数距离 */        let endMove = margin        let endDeg = Math.round(updateDeg / deg) * deg if (type === ‘end‘) { this.setListTransform(endMove, margin) this.setWheelDeg(endDeg) } else { this.setListTransform(updateMove, margin) this.setWheelDeg(updateDeg) } // other code ....
Inertia Slow motion
Other code ..... setwheeldeg (updatedeg, type, time =1000) {if (type = = =' End ') {this. $refs. wheel.style.webkitTransition =' Transform${time}ms Cubic-bezier (0.19, 1, 0.22, 1) 'this. $refs. Wheel.style.webkitTransform =' Rotate3d (1, 0, 0,${updatedeg}deg) '}else {this. $refs. wheel.style.webkitTransition =‘‘this. $refs. Wheel.style.webkitTransform =' Rotate3d (1, 0, 0,${updatedeg}deg) '}}setlisttransform (Translatey =0, margintop =0, type, time =1000) {if (type = = =' End ') {this. $refs. list.style.webkitTransition =' Transform${time}ms Cubic-bezier (0.19, 1, 0.22, 1) 'this. $refs. List.style.webkitTransform =' Translatey (${translatey-This.spin.branch *34}PX) 'this. $refs. List.style.marginTop =`${-margintop}px ' this. $refs. List.setattribute ( ' scroll ', translatey) console.log ( ' End ')} Span class= "Hljs-keyword" >else {this. $refs. list.style.webkitTransition = " this. $refs. list.style.webkitTransform =  Translatey (${translatey-this.spin.branch * 34}px" this. $refs. List.style.marginTop =  Span class= "Hljs-subst" >${-margintop}px ' this. $refs. List.setattribute ( scroll ', Translatey)}}//other code ...      
Gets the currently selected value
/* 在设置完css后获取值  */ setStyle (move, type, time) {   // ...other code   /* 设置$emit 延迟 */   setTimeout(() => this.getPickValue(endMove), 1000) // ...other code}/* 获取选中值 */ getPickValue (move) { let index = Math.abs(move / 34) let pickValue = this.getSpinData(index) this.$emit(‘input‘, pickValue) }
Initialize settings
Mounted () {/* Event Binding */this. $el. AddEventListener (' Touchstart ',This.itemtouchstart)this. $el. AddEventListener (' Touchmove ',This.itemtouchmove)this. $el. AddEventListener (' Touchend ', this.itemtouchend)/ * Initialization status * /Let index = this.listData.indexOf (this.value) if (Index = = -1) { Console.warn (' current initial value does not exist, please check the rear listdata range!! ') this.setlisttransform () This.getpickvalue (0)} else {let move = index * +/*  due to top slip * * This.setstyle (-move) this.setlisttransform (-move,-move)}      
When displayed as a non-infinite wheel

Here we have a good judgment, that is, the distance of the scroll must not exceed the original number of the array length *34, and cannot be less than 0 (the actual code involved in the direction)

/* Determine updatemove maximum distance according to the roller type line or cycle */if (This.type = = =' line ') {if (Updatemove > 0) {Updatemove = 0} if (Updatemove <-(this.listData.length- 1) * Single Height) {updatemove =-(this.listData.length- 1) * Singleheight}} /* control wheel display effect by type */Sethidden (index) { if (this.type = = = ' line ') { return index < 0 | | index > this.listData.length- 1 } else { return false}},             

The DOM structure also adds a corresponding response

<Divclass="Pd-select-item" ><Divclass="Pd-select-line" ></Div><Divclass="Pd-select-list" ><Ulclass="Pd-select-ul"Te ="List" ><Liclass="Pd-select-list-item"V-for="El,index in RenderData": class="{' Hidden ': Sethidden (El.index)}": key="Index" >{{el.value}}</Li></Ul></Div><Ulclass= "Pd-select-wheel" ref= "wheel" > Span class= "Hljs-tag" ><li class= " Pd-select-wheel-item ":class=" {' Hidden ': Sethidden (El.index)} " :style= "setwheelitemdeg (el.index)" : Index= "El.index" v-for= "El,index in RenderData ":key=" index ">{{el.value}} </li> </ ul> </DIV>        

Vue implements iOS native picker effect (Implementation thinking analysis)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.