5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DOM要素をカーソルの位置で拡大縮小、移動可能にする【JavaScript】

Last updated at Posted at 2021-08-09

##したいこと

  • 要素をマウスホイールで拡大縮小可能に
  • 拡大縮小はカーソルを中心に
  • 要素をドラッグで移動可能に
  • 親要素をダブルクリックでデフォの状態に

##コード
###HTML

<div id="parent">
  <div id="child">  
    <img src="http://placehold.jp/300x200.png">
  </div>
</div>

###CSS

#parent, #child {
  width: 300px;
  height: 200px;
}
#parent {
  position: relative;
  overflow: hidden;
}
#child {
  cursor: move;
}

###JavaScript

window.onload = () => {
  let child = document.getElementById("child");
  zoomable(child);
  moveable(child);
  resettable(child);
}
let zoomable = (element, options) => {
    let {minScale, maxScale, magnification} = {
        minScale: 0.5, 
        maxScale: 2, 
        magnification: -0.0025,
        ...options
    }
    let scale = 1;
    element.style.transform = "scale(1)";

    let zoomElement = (event) => {
        event.preventDefault(); // Do not scroll the page

        const w1 = element.clientWidth;
        const h1 = element.clientHeight;

        // currently scale
        let s1 = Number(/(?<=\().*?(?=\))/.exec(element.style.transform));
        // gap with the parent element
        let gX = Number(/.*?(?=px)/.exec(element.style.left));
        let gY = Number(/.*?(?=px)/.exec(element.style.top));
        // center coordinate of the image
        let cX = w1/2 + gX;
        let cY = h1/2 + gY;
        // coordinate of the pointer
        let pX = event.pageX - element.parentElement.offsetLeft;
        let pY = event.pageY - element.parentElement.offsetTop;
        // distance between the center and the pointer
        let dX = pX - cX;
        let dY = pY - cY;

        // scale the image and move to correct coordinate
        scale += event.deltaY * magnification;
        scale = Math.min(Math.max(minScale, scale), maxScale);
        element.style.left = gX + dX * (1 - scale/s1) + "px";
        element.style.top = gY + dY * (1 - scale/s1) + "px";
        element.style.transform = `scale(${scale})`;
    };

    element.addEventListener("wheel", zoomElement);
}
let moveable = (element) => {
  element.style.position = "absolute";
  let x1;
  let y1;
  let px1;
  let py1;
  
  let moveElement = (event) => {
    // Do not drag the image
    event.preventDefault();
    element.ondragstart = false; 
    
    element.style.left = x1 + event.pageX - px1 + "px";
    element.style.top = y1 + event.pageY - py1 + "px";
  };
  
  element.addEventListener("mousedown", (event) => {
    x1 = Number(/.*?(?=px)/.exec(element.style.left));
    y1 = Number(/.*?(?=px)/.exec(element.style.top));
    px1 = event.pageX;
    py1 = event.pageY;
    
    element.addEventListener("mousemove", moveElement);
  });
  element.addEventListener("mouseup", () => {
    element.removeEventListener("mousemove", moveElement);
  });
}
let resettable = (element) => {
  let resetElement = (event) => {
    event.preventDefault();
    
    element.style.transform = "scale(1)";
    element.style.left = 0;
    element.style.top = 0;
  };
  
  element.parentElement.addEventListener("dblclick", resetElement);
}

ポイント

  • addEventListener
  • 第2引数に無名関数を登録するとremoveEventListenerできないため、関数オブジェクトを変数に代入して使う
  • 第2引数の関数に引数を設定するとremoveEventListenerできないため、関数に渡したい値はaddEventListenerの1つ上のブロックで定義してまるごと関数にしてしまう
  • Dom操作
  • element.style.propertyはDOM要素のstyle属性なので、CSS(style要素)を参照できない
  • そのため依存関係にあるプロパティはstyle属性にHTMLかJSで直接記述する必要がある
  • その他
  • ユーザー操作を可能にするにはデフォの操作を消す必要がある
  • element.style.propertyから数値を取得するときに単位消して型変換にする

カーソルを中心に拡大縮小

くっそわかりずらいしどうせ忘れるのでメモ。

  • transform: scale()は画像の上下左右中央を中心に拡大縮小される
  • left, top(position: absolute)は要素が等倍のときの親要素との差
  • 肝のとこは画像の中央の座標とカーソルの座標の拡大前後の差最初の親要素との差を足してtop, leftに設定してる
  • どういうことかっていうとイメージとしてはまずカーソルの位置に画像の中央を移動させて拡大。その後、拡大前にカーソルがあった部分をもう一度カーソルの位置に移動させているといった感じ。
  • 一次元ならカーソルを原点から1の位置として画像の中央は2、これを-1だけ(画像の中央をカーソルの位置に)動かして(例えば)2倍に拡大すると、当然もともとカーソルがあった画像内の点は0(=1-1)から-1=(1-(1-0)*2)。-1動かして2倍かけてるから+2だけ動かせば画像の点はカーソルの位置でそこだけ同じになる。
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?