商品画像の詳細/拡大表示のjs実装サンプル(ECサイトとかでよくあるやつ)

10/03/2019

概要

アパレル系とかに多いようなイメージですがに某アマゾンとかにも
あるような商品画像の拡大イメージ表示するjsの実装サンプルです。

これ系はライブラリとかも種類も結構あるので、需要あるのか何ともですが
フロントjs実装の仕事やってると多分それなりに出くわす事ある気がします。
昔仕事でやったソース引っ張り出してサンプル作成してみました。

個人的にはライブラリ入れて済むような物はそれはそれでいいのですが、
追加の要望とかが来てカスタムする必要が出てきりのパターンも多く、
ライブラリの内容追ったりの作業が結構大変で
結局自前で組んだほうが工数少なく済んだりするので
極力自力で作ります。
slickとかは良く使うけども、

商品画像詳細拡大表示のサンプル

See the Pen ECサイト等でよくある商品画像詳細拡大表示のサンプル by admin@nocebo.jp (@nocebojp) on CodePen.

codepenのサンプルです。
多分実際の案件だとIEとかスマホの調整もいると思うのですが、
そのあたりは書かかない方がサンプルとしてわかりやすいような気がしたので、確認してません。

面倒なのもあるけど

以下多少ですが軽く説明等入れてみます。
あと何となくjquery利用しないでやってみました。
Vanilla JSってまだ呼んだりするのだろうか?

htmlの補足

多分特に無し

とりあえずhtmlに関しては特に言うことないと思う。

css(scss)の補足

レンズ位置の調整

// .zoom__lens
top: -$lenzSize * .5;
left: -$lenzSize * .5;

// .zoom__result
top: $lenzSize * .5;
left: $lenzSize * .5;

cssは一点レンズの位置調整だけ補足
zoom__lensとかzoom__resultの位置を
レンズの幅(半径)に合わせて位置調整してます。

zoom__lensをマウスの中心合わせにして
調整した分をzoom__resultで付加という感じです。

多分jsでやるより分かりやすい。

javascript(babel)の補足

Pointクラス

xとyの値もってるだけですが、一応ポイントクラス

よくあるやつ部分的にもってきた物です。

class Point{
	constructor(x = 0, y = 0) {
		this.x = x || 0;
		this.y = y || 0;
	}
}

Zoomクラス

こっちが今回メインのクラスです。

constructor

constructor内で入れてる値はコメントのまんまです。

this.isMouseEnter = false;

this.baseSize = 500; // 拡大用画像のサイズとりあえず1:1のサイズのみ

this.scale = 2.0; // スケール初期値
this.scaleMin = 1.0; // スケール最小値
this.scaleMax = 3.0; // スケール最大値
this.scaleNum = 0.02; // マウススクロールでのスケール変化値

this.factor = 0.10; // イージング 係数
this.fraction = 0.05; // マウス位置の繰り上げ・切り下げ 端数

学生時代数学とか物理とか苦手だったので

ここに限らず色々用語合ってるのか不明ですが

wikiで調べたから多分合ってる?ハズ。

mouseenterとmouseleave

this.inner.addEventListener("mouseenter",(e) => {
    this.isMouseEnter = true;
}, false);
this.inner.addEventListener("mouseleave",(e) => {
    this.isMouseEnter = false;
}, false);

画像の上にマウスがある場合に処理します。

因みに今回のサンプルはPCというかクロームでの動作しか確認してませんです。

リサイズ

window.addEventListener("resize", this.resize.bind(this), false);
~~
resize(){
    this.elmRect = this.elm.getBoundingClientRect();
}

リサイズで写真要素のgetBoundingClientRect更新します。

window内の要素位置っていう認識?でよいかと

この後のmousemoveで利用します。

mousemove

// baseSizeに合わせてマウス位置を0.0 ~ 1.0に正規化
var x = (e.clientX - this.elmRect.left) / this.baseSize;
var y = (e.clientY - this.elmRect.top) / this.baseSize;

// 繰り上げ/繰り下げ
if(x < 0 + this.fraction) x = 0;
if(x > 1 - this.fraction) x = 1;
if(y < 0 + this.fraction) y = 0;
if(y > 1 - this.fraction) y = 1;

マウスムーブでマウスのX/Yの位置取得して
baseSize利用して0.0〜1.0の数値に正規化します。
別にしなくてもいいのだけど多分した方がわかりやすい、と思う。
elmRectはリサイズで更新した。画像のwindow内の位置です。

あとe.clientX/Yがmouseleave中途半端な数値になったりするので、
(0.01とか0.985とか)
fractionで繰り上げ/繰り下げです。
fractionって単語が使い方的に合ってるかどうかは知らない。

京の生まれやさかい、よその事はよう知りまへん。

mousewheel

let delta = e.deltaY ? -(e.deltaY) : e.wheelDelta ? e.wheelDelta : -(e.detail);

if (delta < 0){
   this.scale -= this.scaleNum;
} else if (delta > 0){
   this.scale += this.scaleNum;
}

if(this.scale < this.scaleMin) this.scale = this.scaleMin;
if(this.scale > this.scaleMax) this.scale = this.scaleMax;

ざっくりいうと上にスクロールで拡大、
下にスクロールで縮小です。
scaleMin/Maxはこれ以上はもぅマヂ無理。っていう拡大の最小値と最大値です。

update

あと最後にというかupdateの処理です。
上記までに色々取得した数値利用してレンズの位置とか画像拡大等更新します。

window.requestAnimationFrame(this.update.bind(this));

// 位置更新のイージング処理
this.currentPoint.x += Number(this.targetPoint.x - this.currentPoint.x) * this.factor;
this.currentPoint.y += Number(this.targetPoint.y - this.currentPoint.y) * this.factor;

// レンズの位置
var lenzPos = new Point((this.currentPoint.x * this.baseSize),(this.currentPoint.y * this.baseSize));
Object.assign(this.lens.style,{
  transform: `translate(${lenzPos.x}px, ${lenzPos.y}px)`
});

// 拡大画像の位置
var resultPos = new Point(
                    (this.currentPoint.x * this.baseSize * this.scale) - this.baseSize * this.currentPoint.x + lenzPos.x,
                    (this.currentPoint.y * this.baseSize * this.scale) - this.baseSize * this.currentPoint.y + lenzPos.y
                    );
Object.assign(this.zoom__result.style,{
  width: this.baseSize + "px",
  transform: `translate(-${resultPos.x}px, -${resultPos.y}px) scale(${this.scale})`
});

これはもう上記で結構そのままなのですが、
factorで現在位置(currentPoint)イージングして
Object.assignでスタイル適用してます。

resultPosが拡大画像の位置とスケールですが、
今回result要素がlenz要素の入れ子になっているので、
レンズ位置の移動分位置調整してます。
svgとかでマスクしたら不要かもです。

以上です。

今後時間ある時にIEとか、

スマホのタッチムーブとかピンチin/outのサンプルとかも書くかもです。

かもですが。

どなたかの参考/お役になりましたら幸いでございます。

javascript

Posted by admin