やりたいこと
- google mapのように「スクロールしたとき、マウスの位置を中心に拡大する」機能を作りたい。
- 画像の拡大縮小ライブラリは存在するが、それ以外のDOMでも同様に動くようにしたい
コード
html
<!-- boardが枠 / boxが拡大される本体 -->
<div id="board">
<div id="box" >
<img id="image" src="image.png">
<div id="dom"></div>
</div>
</div>
css
#board {
width:500px;
height:500px;
border:3px solid green;
overflow: hidden;
}
#box{
width:500px;
height: 500px;
transition-duration: 200ms;
}
#image{
width: 500px;
height: 500px;
}
#dom{
position: absolute;
top:30px;
left:30px;
width: 30px;
height: 30px;
background:red;
}
この辺は適当に自分の作りたいものに合わせて
javascript
const board=document.getElementById('board')
const box=document.getElementById('box')
let scale=1
let pos={
x:0,
y:0,
}
box.style.transformOrigin="0 0"
board.addEventListener('wheel',(event)=>{
event.preventDefault()
let mousePos={
x:event.clientX,
y:event.clientY,
}
if(event.deltaY>0){
scale*=0.9
pos.x=pos.x*0.9+mousePos.x*0.1
pos.y=pos.y*0.9+mousePos.y*0.1
}else if(event.deltaY<0){
scale*=1.1
pos.x=pos.x*1.1-mousePos.x*0.1
pos.y=pos.y*1.1-mousePos.y*0.1
}
box.style.transform=`translate(${pos.x}px,${pos.y}px) scale(${scale})`
},{passive:false})
何をやっているか
wheelイベント
マウスホイールを動かしたときに発火するイベントが存在する。
event.clientX
でイベント発火時のマウスの位置(これは拡大率にかかわらず、画面左端からの距離に依存する)が取得できる
event.deltaY
でページの移動幅(?)のような数値が出る→これが正なら拡大/負なら縮小と判断できる
これを利用して、現在の拡大率を保持しておく→イベント発火→deltaYの正負に基づいて拡大率を調整→要素のstyle.transform属性を書き換え の流れでホイールと同時に要素を拡大できる
位置調整
計算のしやすさの観点から、transformOriginを (0,0)
(左上)にして話を進める
純粋に「ホイールの分だけscaleを大きくする」実装をすると、左上を中心に拡大されてしまい、想定と違う動きをする
そこで、scaleと同時にtranslateを使ってマウスの位置が拡大の中心になるように(無理やり)調節する
具体的な計算式について
拡大によって生じるずれは、要素端からのマウスの位置の1.1倍(今回の場合)なので、要素のtranslateXを保持しておき、event.clientXからtranslateXを引いたものに1.1をかけた値を補正値として、translateX分に加算すればよい
あとはごにょごにょ計算すると次の式が出てくる。
要素のtranslateX = 要素のtranslateX*1.1 - マウスのevent.clientX * 0.1
縮小及びyについても同様の考えで式を出せば完成