/**
 * 滚轮类,是显示选择器可选项的滚轮体，3D模式型的，有透视效果
 */ import $ from '../util/domUtil' ; import { em } from '../em' ; import animationUtil from '../util/animationUtil' ; import constant from '../constant' ; import { Col } from '../Col' ; import { Picker } from '../Picker' ; import { IOptions } from '../API' ; import { AWheel } from './AWheel' ; import { MyJQuery } from 'my-jquery/types/MyJQuery' ; declare function require < T > ( name ) ; const perspectiveConversion = require < { ( y , radius , wheelHeight ) : number ; } > ( "./perspectiveConversionCache" ) ; const tick = require < { ( ) : { play ( ) ; } ; } > ( "../tick/tick" ) ( ) ; export class Wheel3D extends AWheel { private maxAngle = 0 ; private minAngle = 0 ; private radius = constant . WHEEL_HEIGHT / Math . sqrt ( 5 ) ; private visibleAngle = 0 ; private em : ( ) => number = em ; private offsetTop = 0 ; private originalAngle = 0 ; private lastIndexAngle = 0 ; private changeMaxAngle = 0 ; private angle = 0 ; private animationId = - 1 ; private speed = 0 ; private timeStamp = 0 ; private lastY = 0 ; private isDraging = false ; constructor ( picker , col , option , index ) { super ( ) ; this . picker = picker ; this . option = option ; this . index = index ; this . dom = $ ( '<div class="picker-wheel3d">' + '<div class="picker-label"><span class="picker-text"></span></div>' + '<ul></ul>' + '<div class="picker-label"><span class="picker-text"></span></div>' + '</div>' ) . css ( 'height' , constant . WHEEL_HEIGHT / 100 + 'em' ) ; this . contains = this . dom . find ( 'ul' ) ; this . visibleAngle = 90 - ( Math . acos ( this . radius / constant . WHEEL_HEIGHT * 2 ) / Math . PI * 180 ) ; this . labelKey = col . labelKey ; this . itemValueKey = col . valueKey ; var that = this ; function startDrag ( event ) { var target = event . currentTarget ; that . offsetTop = 0 ; while ( target ) { that . offsetTop += target . offsetTop ; var target = target . parentElement ; } var offsetY = event . touches ? event . touches [ 0 ] . clientY - that . offsetTop : event . clientY - that . offsetTop ; that . startDrag ( offsetY ) ; } this . dom [ 0 ] . addEventListener ( "touchstart" , startDrag ) ; this . dom [ 0 ] . addEventListener ( "mousedown" , startDrag ) ; function drag ( event ) { var offsetY = event . touches ? event . touches [ 0 ] . clientY - that . offsetTop : event . clientY - that . offsetTop ; that . drag ( offsetY ) ; } this . dom [ 0 ] . addEventListener ( "touchmove" , drag ) ; this . dom [ 0 ] . addEventListener ( "mousemove" , drag ) ; function endDrag ( ) { that . endDrag ( ) ; } this . dom [ 0 ] . addEventListener ( "touchend" , endDrag ) ; this . dom [ 0 ] . addEventListener ( "mouseup" , endDrag ) ; this . dom [ 0 ] . addEventListener ( "mouseleave" , endDrag ) ; this . initMouseWheel ( ) ; let transformValue = `translateZ(${ this . radius / 100 }em) scale(0.75)` ; this . dom . find ( ".picker-label" ) . css ( "-webkit-transform" , transformValue ) . css ( "transform" , transformValue ) ; this . setSuffix ( col . suffix ) ; this . setPrefix ( col . prefix ) ; this . setOptions ( col . options , null , true ) ; } param { number } offsetY protected startDrag ( offsetY ) { this . lastY = ( constant . WHEEL_HEIGHT / 2 - offsetY / this . em ( ) ) * - 1 ; this . timeStamp = Date . now ( ) ; this . isDraging = true ; this . offsetTop = this . dom [ 0 ] . offsetTop ; this . originalAngle = this . angle ; this . changeMaxAngle = 0 ; this . lastIndexAngle = this . selectedIndex ; for ( var parent = this . dom [ 0 ] . parentElement ; parent ; parent = parent . parentElement ) { this . offsetTop += parent . offsetTop ; } animationUtil . stopAnimation ( this . animationId ) ; } param { number } offsetY protected drag ( offsetY ) { if ( ! this . isDraging ) { return ; } var y = ( constant . WHEEL_HEIGHT / 2 - offsetY / this . em ( ) ) * - 1 ; var changeAngle = ( perspectiveConversion ( this . lastY , this . radius , constant . WHEEL_HEIGHT ) - perspectiveConversion ( y , this . radius , constant . WHEEL_HEIGHT ) ) / Math . PI * 180 ; var angle = changeAngle + this . angle ; this . changeMaxAngle = Math . max ( Math . abs ( this . originalAngle - angle ) , this . changeMaxAngle ) ; this . setAngle ( angle ) ; this . lastY = y ; if ( changeAngle ) { this . speed = changeAngle / ( Date . now ( ) - this . timeStamp ) ; } else { this . speed = 0 ; } this . timeStamp = Date . now ( ) ; } /**
     * 拖拽结束
     */ protected endDrag ( ) { if ( ! this . isDraging ) { return ; } var changeAngle = this . speed * Math . abs ( this . speed ) * 8 * constant . WHEEL_TRANSITION_TIME ; var angle = changeAngle + this . angle ; var selectedIndex = this . calcSelectedIndexByAngle ( angle ) ; this . selectIndex ( selectedIndex , true ) ; this . isDraging = false ; this . lastY = 0 ; this . speed = 0 ; } param { any [ ] } list param { * } selectedValue param { boolean } isInti setOptions ( list = [ ] , selectedValue , isInti = false ) { var that = this ; if ( ! Array . isArray ( list ) ) { throw new TypeError ( "list is not a array." ) ; } let oldLength = this . list && this . list . length || 0 ; const lis = this . contains . children ( ) ; if ( oldLength > list . length ) { lis . each ( ( i , e ) => { if ( i >= list . length ) { $ ( e ) . remove ( ) ; } } ) ; } this . list = list ; this . valueHashMap = { } ; this . maxAngle = constant . WHEEL_ITEM_ANGLE * ( Math . max ( 0 , this . list . length - 1 ) ) ; var label , height = this . radius * Math . PI * constant . WHEEL_ITEM_ANGLE / 180 ; this . list . forEach ( function ( item , index ) { if ( typeof item === 'object' ) { label = item [ that . labelKey ] ; that . valueHashMap [ item [ that . itemValueKey ] ] = index ; } else { label = item ; that . valueHashMap [ item ] = index ; } if ( index < oldLength ) { let span = lis . eq ( index ) . find ( 'span' ) ; if ( span . text ( ) != label ) { span . text ( label ) ; } } else { var li = $ ( "<li></li>" ) ; li . append ( $ ( '<span class="picker-text"></span>' ) . text ( label ) ) ; var angle = constant . WHEEL_ITEM_ANGLE * - index ; var transformValue = "rotateX(" + angle + "deg) translateZ(" + that . radius / 100 + "em) scale(0.75)" ; li . css ( "-webkit-transform" , transformValue ) . css ( "transform" , transformValue ) . css ( "padding" , `${ height / 5.9 / 100 }em 0` ) . css ( "height" , height / 100 + "em" ) . css ( "line-height" , height / 100 + "em" ) ; li . data ( "angle" , angle ) ; li . data ( "index" , index ) ; var clickHandle = function ( event ) { if ( that . changeMaxAngle < 1 ) { that . isDraging = false ; that . lastY = 0 ; that . speed = 0 ; that . selectIndex ( index , true ) ; event . stopPropagation ( ) ; event . preventDefault ( ) ; } } ; li [ 0 ] . addEventListener ( 'mouseup' , clickHandle ) ; li [ 0 ] . addEventListener ( 'touchend' , clickHandle ) ; that . contains . append ( li [ 0 ] ) ; } } ) ; this . flushLabel ( ) ; if ( isInti ) { if ( list . length > 0 ) { this . selectedIndex = 0 ; if ( typeof list [ 0 ] === 'object' ) { this . selectedValue = this . list [ 0 ] [ this . itemValueKey ] ; } else { this . selectedValue = this . list [ 0 ] ; } } else { this . selectedIndex = - 1 ; this . selectedValue = undefined ; } return ; } if ( list . length > 0 ) { if ( selectedValue != null && this . valueHashMap [ selectedValue ] != null ) { this . selectOption ( selectedValue ) ; } else if ( this . valueHashMap [ this . selectedValue ] != null ) { this . selectOption ( this . selectedValue ) ; } else { this . selectIndex ( 0 ) ; } } else { this . selectedIndex = - 1 ; this . selectedValue = undefined ; } } param index param showAnimation protected selectIndex ( index , showAnimation = false ) { var angle = this . calcAngleBySelectedIndex ( index ) ; animationUtil . stopAnimation ( this . animationId ) ; if ( showAnimation ) { var start = 0 , during = 50 , that = this ; var _run = function ( ) { start ++ ; var _angle = animationUtil . easeOut ( start , that . angle , angle - that . angle , during ) ; if ( Math . abs ( _angle - angle ) < 1 ) { _angle = angle ; } that . setAngle ( _angle ) ; if ( _angle != angle ) { that . animationId = animationUtil . startAnimation ( _run ) ; } else { var oldSelectedIndex = that . selectedIndex ; that . selectedIndex = index ; that . selectedValue = that . list [ index ] ; if ( typeof that . selectedValue == 'object' ) { that . selectedValue = that . selectedValue [ that . itemValueKey ] ; } if ( oldSelectedIndex != that . selectedIndex ) that . toggleSelected ( that . selectedIndex , that . selectedValue ) ; } } ; that . animationId = animationUtil . startAnimation ( _run ) ; } else { var oldSelectedIndex = this . selectedIndex ; this . setAngle ( angle ) ; this . selectedIndex = index ; this . selectedValue = this . list [ index ] ; if ( typeof this . selectedValue == 'object' ) { this . selectedValue = this . selectedValue [ this . itemValueKey ] ; } if ( oldSelectedIndex != this . selectedIndex ) this . toggleSelected ( this . selectedIndex , this . selectedValue ) ; } } param { number } angle returns { number } private setAngle ( angle ) { angle = this . rangeAngle ( angle ) ; if ( this . option . hasVoice && this . picker . visible ) { var lastIndexAngle = this . lastIndexAngle ; var index = this . calcSelectedIndexByAngle ( angle ) ; if ( lastIndexAngle != index ) { if ( this . option . hasVoice ) { tick . play ( ) ; } } this . lastIndexAngle = index ; } this . contains . css ( "-webkit-transform" , "rotateX(" + angle + "deg)" ) . css ( "transform" , "rotateX(" + angle + "deg)" ) ; this . angle = angle ; this . flushLabel ( ) ; return angle ; } param angle { number } returns { number } private calcSelectedIndexByAngle ( angle ) { angle = this . rangeAngle ( angle ) ; return Math . round ( Math . abs ( angle / constant . WHEEL_ITEM_ANGLE ) ) ; } param angle { number } returns { number } private calcAngleBySelectedIndex ( index ) { return index * constant . WHEEL_ITEM_ANGLE ; } param angle { number } returns { number } private rangeAngle ( angle ) { angle = Math . max ( this . minAngle , angle ) ; angle = Math . min ( this . maxAngle , angle ) ; return angle ; } /**
    * 刷新各个标签的状态,确定应该显示哪些标签
    */ private flushLabel ( ) { var that = this ; this . dom . find ( "li" ) . each ( function ( index , li ) { li = $ ( li ) ; var angle = li . data ( "angle" ) + that . angle ; if ( angle > that . visibleAngle || angle < ( - that . visibleAngle ) ) { if ( li . css ( "display" ) != "none" ) { li . css ( "display" , "none" ) ; } } else { if ( li . css ( "display" ) != "block" ) { li . css ( "display" , "block" ) ; } } } ) ; } param text private setSuffix ( text ) { this . dom . find ( '.picker-label .picker-text' ) . eq ( 1 ) . text ( text ) ; } param text private setPrefix ( text ) { this . dom . find ( '.picker-label .picker-text' ) . eq ( 0 ) . text ( text ) ; } }