iPhoneサイトのモックアップ用にフリックとピンチのJSを作ったのでメモ。
かなり急いで作ったからスパゲッティになってしまった。
写真を見せるギャラリーサイトを想定。
フリックで別の写真へ移動し、ピンチで拡大する。
※別途YUIライブラリ ver:3.0.0が必要です
JavaScript
document.onscroll = function(){ if(window.pageXOffset > 5)scrollTo(0,1); } YUI().use('anim', function(Y) { //アニメーションインスタンス生成 var collectionAnim = new Y.Anim({ node: '#collection', duration: 1, easing: Y.Easing.easeOut }); //イベント登録 var colectionLoaded = function(e) { //フェードイン collectionAnim.set('to', { opacity:1 }); collectionAnim.run(); var PHOTO_WIDTH = 320; var PHOTO_HEIGHT = 427; var PHOTO_MAX = 6; var SCROLL_X_MIN = - PHOTO_WIDTH * (PHOTO_MAX - 1); var startX = 0;//#collectionのタッチ時のx座標 var nowX = 0;//#collectionのタッチ中のx座標 var scroll = 0;//タッチの移動距離 var target = 0;//#collectionの移動先 var startTouch = 0;//タッチ時のタッチ位置のx座標 var nowTouch = 0;//タッチ中のタッチ位置のx座標 var startDis = 0;//タッチ時の2タッチ間の距離 var nowDis = 0;//タッチ中の2タッチ間の距離 var startPhotoWidth = 0;//タッチ時の写真幅 var startPhotoHeight = 0;//タッチ時の写真高さ var touchCount = 0;//タッチ数 var photoCount = 0;//現在表示している写真のカウント var collectionElement = document.getElementById("collection"); var zoom = false;//拡大中フラグ var moving = false;//移動中フラグ //タッチイベントリスナー登録 collectionElement.addEventListener('touchstart', touchHandler, false); collectionElement.addEventListener('touchmove', touchHandler, false); collectionElement.addEventListener('touchend', touchHandler, false); collectionElement.addEventListener('touchcancel', touchHandler, false); //タッチイベント処理 function touchHandler(e) { e.preventDefault(); if (e.type == "touchstart") { document.getElementById("touchstatus").innerHTML = "タッチスタート"; //タッチ数が1の時でピンチしていない時で移動中じゃないとき if (e.touches.length == 1 && zoom == false && moving == false) { touchCount = 1; startTouch = e.touches[0].pageX; scroll = 0; startX = collectionElement.offsetLeft; document.getElementById("touchmove").innerHTML = "startTouch:" + startTouch + " startX:" + startX; } //タッチ数が2(ピンチ)の時で移動中じゃないとき if (e.touches.length == 2 && moving == false) { touchCount = 2; zoom = true; var touch1 = e.touches[0]; var touch2 = e.touches[1]; //3平方の定理で最初の距離を取得 startDis = Math.sqrt(Math.pow((touch1.pageX - touch2.pageX), 2)+Math.pow((touch1.pageY - touch2.pageY), 2)); var targetPhoto = document.getElementById("photo"+photoCount); startPhotoWidth = targetPhoto.offsetWidth; startPhotoHeight = targetPhoto.offsetHeight; } }else if (e.type == "touchmove") { document.getElementById("touchstatus").innerHTML = "タッチ移動"; document.getElementById("touchx").innerHTML = e.touches[0].pageX; document.getElementById("touchy").innerHTML = e.touches[0].pageY; //タッチ数が1の時でピンチしていない時で移動中じゃないとき if (e.touches.length == 1 && zoom == false && moving == false) { touchCount = 1; nowTouch = e.touches[0].pageX; scroll = nowTouch - startTouch; target = startX + scroll; //ステージからはみ出ないようにする if(target > 0){ target = 0; }else if(target < SCROLL_X_MIN){ target = SCROLL_X_MIN; } collectionElement.style.left = target + "px"; document.getElementById("touchmove").innerHTML = "nowTouch:" + nowTouch + " scroll:" + scroll + " target:" + target; } //タッチ数が2(ピンチ)の時で移動中じゃないとき if (e.touches.length == 2 && moving == false) { touchCount = 2; var touch1 = e.touches[0]; var touch2 = e.touches[1]; //3平方の定理で現在の距離を取得 nowDis = Math.sqrt(Math.pow((touch1.pageX - touch2.pageX), 2)+Math.pow((touch1.pageY - touch2.pageY), 2)); //倍率 var magnifications = nowDis / startDis; var targetPhoto = document.getElementById("photo"+photoCount); if(startPhotoHeight * magnifications > PHOTO_HEIGHT){ //拡大 targetPhoto.style.width = startPhotoWidth * magnifications + "px"; targetPhoto.style.height = startPhotoHeight * magnifications + "px"; //位置調整 collectionElement.style.left = -(PHOTO_WIDTH * photoCount + (startPhotoWidth * magnifications - PHOTO_WIDTH) / 2) + "px"; collectionElement.style.top = -(startPhotoHeight * magnifications - PHOTO_HEIGHT) / 2 + "px"; zoom = true; }else{ //等倍 targetPhoto.style.width = PHOTO_WIDTH + "px"; targetPhoto.style.height = PHOTO_HEIGHT + "px"; //正常位置 collectionElement.style.left = -PHOTO_WIDTH * photoCount + "px"; collectionElement.style.top = 0 + "px"; zoom = false; } document.getElementById("touchmove").innerHTML = "2タッチ間の距離:" + nowDis + " 倍率" + magnifications; } }else if (e.type == "touchend" || e.type == "touchcancel") { document.getElementById("touchstatus").innerHTML = ""; //タッチ数が1の時でピンチしていない時で移動中じゃないとき if(touchCount == 1 && zoom == false && moving == false){ if(scroll < 0){ photoCount++ if(photoCount >= PHOTO_MAX - 1){photoCount = PHOTO_MAX - 1;} }else{ photoCount-- if(photoCount <= 0){photoCount = 0;} } moving = true; collectionAnim.set('to', { xy: [-photoCount * PHOTO_WIDTH, 0] }); collectionAnim.run(); collectionAnim.on('end', function(){moving = false;}); document.getElementById("touchend").innerHTML = "photoCount:" + photoCount; } touchCount = 0; } } }; Y.on('load', colectionLoaded); });
HTML
<div id="collection"> <ul> <li class="item"><img id="photo0" src="01.jpg" width="320" height="417" /></li> <li class="item"><img id="photo1" src="02.jpg" width="320" height="417" /></li> <li class="item"><img id="photo2" src="03.jpg" width="320" height="417" /></li> <li class="item"><img id="photo3" src="04.jpg" width="320" height="417" /></li> <li class="item"><img id="photo4" src="05.jpg" width="320" height="417" /></li> <li class="item"><img id="photo5" src="06.jpg" width="320" height="417" /></li> </ul> </div><!--#collection--> <div id="debug"> ステータス: <span id="touchstatus"></span> X: <span id="touchx"></span> Y: <span id="touchy"></span> <span id="touchint"></span> <span id="touchmove"></span> <span id="touchend"></span> </div>
CSS
#collection{ opacity:0; width: 9999px; position: absolute; } #collection li{ float: left; } #debug{ display: none; position: absolute; left: 0px; top: 0px; color: #FFF; }
サンプル
http://www.kazy.jp/note/2010/04/yui/
ソース
http://www.kazy.jp/note/2010/04/yui/kazy_jp-note-2010-04-yui.zip