標籤:style blog http java color strong
希望做一個類似IOS拾取器的控制項,在IOS上該控制項的效果是這樣的:,我也把該效果稱之為為輪子效果。
要實現這個效果,能夠用到的技術點非常簡單,無非是transform的translate3d和rotate,不過要想很好的實現,還要建立一個精確的數學模型,來解決如何【擺放】的問題。特別是這個效果不是靜態,需要滿足滑鼠滑動的時,這個輪子要轉起來,這就需要仔細思索了。當然,在最開始重點還是要搞清楚自變數是什麼、因變數是什麼、它們之間的關係是什麼以及該需求的一些性質。找到了好的性質,可以減輕工作量,並且讓代碼可以更加簡潔。
做這個效果,首先想到的,就是用div表示每個小條目,然後想象成這些div是貼在一個圓上面,滾動這個圓。先用html把結構展示一下:
1 <div class="border"> 2 <div class="item">0</div> 3 <div class="item">1</div> 4 <div class="item">2</div> 5 <div class="item">3</div> 6 <div class="item">4</div> 7 <div class="item">5</div> 8 <div class="item">6</div> 9 <div class="item">7</div>10 <div class="item">8</div>11 <div class="item">9</div>12 <div class="item">10</div>13 <div class="item">11</div>14 <div class="item">12</div>15 <div class="item">13</div>16 <div class="item">14</div>17 <div class="item">15</div>18 <div class="item">16</div>19 <div class="item">17</div>20 <div class="item">18</div>21 </div>
接著把其中的css代碼也寫出來:
1 <style> 2 * { 3 -webkit-user-select: none; 4 } 5 6 .item { 7 height: 30px; 8 line-height: 30px; 9 font-size: 15px;10 font-weight: bolder;11 color: #222;12 width: 400px;13 background: white;14 text-align: center;15 position: absolute;16 top: 50%;17 margin-top: -15px;18 }19 20 .border {21 overflow: hidden;22 cursor: pointer;23 position: relative;24 height: 240px;25 width: 400px;26 border: 2px solid black;27 }28 29 .hide {30 display: none;31 }32 </style>
注意上面的border採取的是相對(relative)定位,裡面的條目(item)採用的是絕對位置,而且是垂直置中,這樣的話,就會有如下的效果:
由於給item設定了背景色,所以只會看到最後一個item。接下來我們要做的工作是,把這19個item先擺成像是貼在圓上一樣。這需要先挖掘一下這個需求的性質,因為性質會成為我們的突破口。
想要擺出這個效果,需要先進行分析。我們知道,由於每個item的高度相同,所以每兩個相鄰的item之間的旋轉角度之差相同,而這個角度之差可以通過公式2 * Math.asin( height / 2 / r )計算出來。另外,轉輪的旋轉角度和item的旋轉角度相同,那麼我們需要知道已經旋轉的角度,用這個角度以及角度之差我們可以知道任何一個item的旋轉角度,然後再根據這個角度計算出item擺放的位置。
有了上述的分析,想要擺放出一個很漂亮的拾取器控制項樣式就不那麼難了。不過在此之前,要做一下簡單的準備。
首先,需要一個設定css樣式的javascript函數,因為在擺放的過程中,要頻繁用到javascript來設定css的transform樣式,因此極有必要將該操作提取出來寫成為一個函數,不妨將其命名為css,這部分的代碼如下:
1 function css( el, style ) {2 loopObj( style, function ( name, value ) {3 el.style.setProperty( name, value, "");4 } )5 }
代碼非常簡單,但是非常實用。其中setProperty方法的第三個參數可以為important或者是一個Null 字元串,這裡設成Null 字元串。
接下來進行布局。由於部署在球面上的item具有對稱性,所以對19個item,從第10個開始布局,然後迴圈10次,從中間到兩邊依次對item進行布局。這部分代碼如下所示:
1 var r = 90; 2 var height = 30; 3 var dAngle = 2 * Math.asin( height / 2 / r ); 4 var first = items.length / 2 << 0; 5 6 function setPosition( curAngle ) { 7 d.loop( first + 1, function ( i ) { 8 doSet( items[first + i], curAngle - i * dAngle ); 9 doSet( items[first - i], curAngle + i * dAngle );10 } );11 }
其中doSet的代碼如下所示:
1 function doSet( el, angle ) {2 Math.abs( angle ) > Math.PI / 2 ? el.classList.add( "hide" ) : el.classList.remove( "hide" );3 d.css( el, {4 "-webkit-transform" : "translate3d(0," + (r * Math.sin( -angle )) + "px,0) rotateX(" + 180 * angle / Math.PI + "deg)"5 } );6 }
添加初始化的javascript代碼:setPosition( 0 ),然後在瀏覽器中開啟後就能看到這樣的效果,可以看出布局成功了,正是我們想要的效果。
接下來的工作中,要讓它轉動起來,由於上面考慮的非常充分,已經不必再引入新的變數,只要記錄滑鼠滑動的距離,得出相應的curAngle,然後傳給setPosition即可。相關代碼如下所示:
1 var end = d.Events(); 2 3 d.dragY( border, border, { 4 moving : function () { 5 var moving = d.Events(); 6 moving.regist( function ( e ) { 7 setPosition( -e.dy / 50 ); 8 } ); 9 return moving;10 },11 end : end,12 setMove : function ( e ) {13 console.log( e.dy );14 }15 } );
在瀏覽器中開啟,然後就可以看到已經可以轉動了。
但是這個目前仍然美中不足,就是對旋轉的角度沒有記錄,導致第二次轉動時,還是從新開始,所以還需要改動一下:
1 var alreadyS = 0; 2 3 d.dragY( border, border, { 4 moving : function () { 5 var moving = d.Events(); 6 moving.regist( function ( e ) { 7 console.log( alreadyS - e.dy ); 8 setPosition( (alreadyS - e.dy) / 50 ); 9 } );10 return moving;11 },12 end : function () {13 var end = d.Events();14 end.regist( function ( e ) {15 alreadyS = alreadyS - e.dy;16 } );17 return end;18 },19 setMove : function ( e ) {20 }21 } );
這裡添加了一個全域變數alreadyS,記錄滑動的距離,這樣就不用每次滑動都要重新開始了。更新alreadyS的時機在每次滾動結束結束的時候,所以新添加了end事件。