溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

基于 Vue 的商品主圖放大鏡方案

發(fā)布時(shí)間:2020-07-30 05:03:57 來(lái)源:網(wǎng)絡(luò) 閱讀:314 作者:可樂(lè)程序員 欄目:web開(kāi)發(fā)


前言

在做電商類應(yīng)用時(shí),難免會(huì)遇到商品主圖實(shí)現(xiàn)放大鏡效果的場(chǎng)景,現(xiàn)有的基于Vue的第三方包不多并且無(wú)法直接復(fù)用,今天,我來(lái)分享一種高穩(wěn)定性的基于 Vue 的圖片放大鏡方法。

實(shí)現(xiàn)原理

放大鏡的原理用一句話概括,就是根據(jù)小圖上的鼠標(biāo)位置去定位大圖。

圖1 原理圖(以2倍放大為例)

基于 Vue 的商品主圖放大鏡方案


相信原理圖已經(jīng)畫的很明白了, 圖中,左側(cè)框是小圖框,其藍(lán)色區(qū)域?yàn)閳D片遮罩層(需放大區(qū)域),右側(cè)框是整個(gè)大圖目前所在區(qū)域,其藍(lán)色區(qū)域是放大區(qū)域,設(shè)置超出隱藏,就實(shí)現(xiàn)了放大遮罩區(qū)域的效果。

顯然,兩塊藍(lán)色區(qū)域存在著某種對(duì)應(yīng)關(guān)系,即遮罩的左上角位置(相對(duì)于小圖,以下稱 X 坐標(biāo))和放大區(qū)域(相對(duì)于大圖)的左上角位置是成比例的,即放大倍數(shù)。計(jì)算出 X 坐標(biāo)后,適當(dāng)調(diào)整背景圖的位置,使大圖向反方向移動(dòng) scale 倍的 X 坐標(biāo)即可。

X 坐標(biāo)為(maskX,maskY),以計(jì)算 maskX 為例:

鼠標(biāo)移動(dòng)中會(huì)產(chǎn)生 e.clientX ,標(biāo)識(shí)鼠標(biāo)與瀏覽器左側(cè)的距離,小圖與瀏覽器左側(cè)的距離是 left ,由于遮罩始終是一個(gè)以鼠標(biāo)為中心的正方形,所以:

maskX = e.clientX - left - mask/2

同理,

maskY = e.clientY - top - mask/2

大圖的對(duì)應(yīng)樣式設(shè)置為:

{?left:?-?maskX?*?scale?+?'px';?top:?-?maskY?*?scale?+?'px';
}
復(fù)制代碼

效果演示

圖2 長(zhǎng)圖展示

基于 Vue 的商品主圖放大鏡方案


圖3 寬圖展示

基于 Vue 的商品主圖放大鏡方案


圖4 兩倍放大效果圖

基于 Vue 的商品主圖放大鏡方案


圖5 四倍放大效果圖

基于 Vue 的商品主圖放大鏡方案


核心代碼

HTML

一般放大鏡實(shí)現(xiàn)的是 1:1 等寬等高的正方形圖片,這里兼容了其他比例的圖片,設(shè)置圖片為垂直居中對(duì)齊,包括小圖,大圖。如果小圖不夠充滿整個(gè)小圖框,余留下的空白部分也可以有放大效果,只不過(guò)放大結(jié)果依然是空白。 這樣只需計(jì)算背景圖的移動(dòng)距離,不用過(guò)多的關(guān)注圖片定位問(wèn)題。

<template>
?<div?class="magnifier">
?<!--?小圖?-->
?<div?class="small-box"?@mouseover="handOver"?@mousemove="handMove"?@mouseout="handOut">
?<img?class="smallPic"?:src="`${src}?x-oss-process=image/resize,l_836`"?/>?<div?class="magnifier-zoom"?
?v-show="showMask"
?:style="{
?background:?configs.maskColor,
?height:?configs.maskWidth?+?'px',
?width:?configs.maskHeight?+?'px',?
?opacity:?configs.maskOpacity,?
?transform:?transformMask
?}"
?></div>
?</div>
?<!--?大圖,?注意誤差?-->
?<div?class="magnifier-layer"?
?v-show="showMagnifier"
?:style="{?
?width:?configs.width?+?'px',?
?height:?configs.height?+?'px',?
?left:?configs.width?+?20?+?'px'?
?}"
?>?<div?class="big-box"
?:style="{?
?width:?bigWidth?+?'px',
?height:?bigHeight?+?'px',
?left:?moveLeft,
?top:?moveTop
?}"
?>?<div?class="big-box-img"
?:style="{?
?width:?bigWidth?-?2?+?'px',?
?height:?bigHeight?-?2?+?'px'?
?}"
?>?<img
?:src="bigSrc"
?:style="{?
?maxWidth:?bigWidth?-?2?+?'px',?
?maxHeight:?bigHeight?-2?+?'px'?
?}"
?/>?</div>
?</div>
?</div>
?</div></template>復(fù)制代碼

JS

這里主要有三個(gè)事件函數(shù)。

  • handOver:鼠標(biāo)進(jìn)入到小圖框上的事件,此時(shí)顯示遮罩和放大區(qū)域,并計(jì)算小圖框的位置信息。

handOver()?{?//?計(jì)算小圖框在瀏覽器中的位置
?this.imgObj?=?this.$el.getElementsByClassName('small-box')[0];?this.imgRectNow?=?this.imgObj.getBoundingClientRect();?this.showMagnifier?=?true;?this.showMask?=?true;
}
?
復(fù)制代碼
  • handMove:鼠標(biāo)在小圖上的移動(dòng)事件,此事件發(fā)生在 handOver 之后,計(jì)算數(shù)據(jù),移動(dòng)遮罩以及背景圖;

handMove(e)?{?//?計(jì)算初始的遮罩左上角的坐標(biāo)
?let?objX?=?e.clientX?-?this.imgRectNow.left;
?let?objY?=?e.clientY?-?this.imgRectNow.top;?//?計(jì)算初始的遮罩左上角的坐標(biāo)
?let?maskX?=?objX?-?this.configs.maskWidth?/?2;
?let?maskY?=?objY?-?this.configs.maskHeight?/?2;?//?判斷是否超出界限,并糾正
?maskY?=?maskY?<?0???0?:?maskY;?
?maskX?=?maskX?<?0???0?:?maskX;?
?if(maskY?+?this.configs.maskHeight?>=?this.imgRectNow.height)?{
?maskY?=?this.imgRectNow.height?-?this.configs.maskHeight;
?}?if(maskX?+?this.configs.maskWidth?>=?this.imgRectNow.width)?{
?maskX?=?this.imgRectNow.width?-?this.configs.maskWidth;
?}?//?遮罩移動(dòng)
?this.transformMask?=?`translate(${maskX}px,?${maskY}px)`;?//?背景圖移動(dòng)
?this.moveLeft?=?-?maskX?*?this.configs.scale?+?"px";?this.moveTop?=?-?maskY?*?this.configs.scale?+?"px";
}
復(fù)制代碼
  • handOut:鼠標(biāo)離開(kāi)小圖事件,此時(shí)無(wú)放大鏡效果,隱藏遮罩和放大區(qū)域。

handOut()?{?this.showMagnifier?=?false;?this.showMask?=?false;
}
復(fù)制代碼

以上三個(gè)事件基本上就實(shí)現(xiàn)了圖片的放大鏡功能。

但仔細(xì)看,你會(huì)發(fā)現(xiàn)每次移入小圖框都會(huì)觸發(fā)一次 handOver 事件,并且計(jì)算一次小圖框 DOM (imgObj) 。

為了優(yōu)化此問(wèn)題,可以用 init 標(biāo)識(shí)是否是頁(yè)面加載后首次觸發(fā) handOver 事件,如果是初始化就計(jì)算imgObj 信息,否則不計(jì)算。

handOver()?{?if?(!this.init)?{?this.init?=?true;?//?原?handOver?事件
?...
?}?
?this.showMagnifier?=?true;?this.showMask?=?true;
},
?
復(fù)制代碼

在測(cè)試的過(guò)程中,發(fā)現(xiàn)頁(yè)面滾動(dòng)后,會(huì)出現(xiàn)遮罩定位錯(cuò)誤的情況,原來(lái)是因?yàn)槌跏蓟瘯r(shí),我們固定死了小圖框的位置信息(存放在 this.imgRectNow ),導(dǎo)致 handMove 事件中的移動(dòng)數(shù)據(jù)計(jì)算錯(cuò)誤。

解決這個(gè)問(wèn)題有兩種方案:

  • 監(jiān)聽(tīng) scroll 事件,更新 this.imgRectNow;

  • 在 handMove 事件中更新 this.imgRectNow。

這里選擇了第二種。

handMove(e)?{?//?動(dòng)態(tài)獲取小圖的位置(或者監(jiān)聽(tīng)?scroll?)
?let?imgRectNow?=?this.imgObj.getBoundingClientRect();?let?objX?=?e.clientX?-?imgRectNow.left;?let?objY?=?e.clientY?-?imgRectNow.top;?//?原?handMove?事件剩余內(nèi)容
?...
},
復(fù)制代碼

綜合以上,我們已經(jīng)實(shí)現(xiàn)了一個(gè)完美的圖片放大鏡功能。最終的 js 如下所示:

data()?{?return?{
?imgObj:?{},
?moveLeft:?0,
?moveTop:?0,
?transformMask:`translate(0px,?0px)`,
?showMagnifier:false,
?showMask:false,
?init:?false,
?};
},
computed:?{
?bigWidth(){?return?this.configs.scale?*?this.configs.width;
?},
?bigHeight(){?return?this.configs.scale?*?this.configs.height;
?}
},
methods:?{
?handMove(e)?{?//?動(dòng)態(tài)獲取小圖的位置(或者監(jiān)聽(tīng)?scroll?)
?let?imgRectNow?=?this.imgObj.getBoundingClientRect();
?let?objX?=?e.clientX?-?imgRectNow.left;
?let?objY?=?e.clientY?-?imgRectNow.top;?//?計(jì)算初始的遮罩左上角的坐標(biāo)
?let?maskX?=?objX?-?this.configs.maskWidth?/?2;
?let?maskY?=?objY?-?this.configs.maskHeight?/?2;?//?判斷是否超出界限,并糾正
?maskY?=?maskY?<?0???0?:?maskY;?
?maskX?=?maskX?<?0???0?:?maskX;?
?if(maskY?+?this.configs.maskHeight?>=?imgRectNow.height)?{
?maskY?=?imgRectNow.height?-?this.configs.maskHeight;
?}?if(maskX?+?this.configs.maskWidth?>=?imgRectNow.width)?{
?maskX?=?imgRectNow.width?-?this.configs.maskWidth;
?}?//?遮罩移動(dòng)
?this.transformMask?=?`translate(${maskX}px,?${maskY}px)`;?//?背景圖移動(dòng)
?this.moveLeft?=?-?maskX?*?this.configs.scale?+?"px";?this.moveTop?=?-?maskY?*?this.configs.scale?+?"px";
?},
?handOut()?{?this.showMagnifier?=?false;?this.showMask?=?false;
?},
?handOver()?{?if?(!this.init)?{?this.init?=?true;?this.imgObj?=?this.$el.getElementsByClassName('small-box')[0];
?}?this.showMagnifier?=?true;?this.showMask?=?true;
?}
}
復(fù)制代碼

使用方法

本示例中的固定參數(shù):小圖框:420 * 420 。

程序可接受參數(shù):

//?小圖地址src:?{?type:?String,
},//?大圖地址bigSrc:?{?type:?String,
},//?配置項(xiàng)configs:?{?type:?Object,
?default()?{
?return?{?width:420,//放大區(qū)域
?height:420,//放大區(qū)域
?maskWidth:210,//遮罩
?maskHeight:210,//遮罩
?maskColor:'rgba(25,122,255,0.5)',//遮罩樣式
?maskOpacity:0.6,?scale:2,//放大比例
?};
?}
}
復(fù)制代碼

文中圖 2 是一張長(zhǎng)圖,小圖的最大邊不超過(guò) 836px(二倍圖) ,大圖為了視覺(jué)效果,分辨率盡量高點(diǎn),程序會(huì)根據(jù)配置項(xiàng)自動(dòng)設(shè)置對(duì)應(yīng)的 height , width ,長(zhǎng)圖與寬圖的效果對(duì)比可參考圖3。

配置項(xiàng)可根據(jù)應(yīng)用場(chǎng)景自行設(shè)置,本文示例的配置項(xiàng)是 2 倍放大,效果可參考圖 4,四倍放大效果可參考圖 5。

總結(jié)

其實(shí)圖片放大鏡的實(shí)現(xiàn)思路沒(méi)有那么復(fù)雜,核心點(diǎn)有兩點(diǎn):

  • 小圖、大圖的定位,遮罩和放大區(qū)域的創(chuàng)建方法

  • 放大鏡的原理理解,并用代碼實(shí)現(xiàn) DOM 的移動(dòng)等。

本文順著這個(gè)思路,做了一個(gè)簡(jiǎn)單的實(shí)現(xiàn),還有一些優(yōu)化的空間,歡迎各位大佬在評(píng)論區(qū)討論。雖然代碼看起來(lái)不是非常優(yōu)雅,但是足夠明了,感興趣的同學(xué)可以自己嘗試一下。

基于 Vue 的商品主圖放大鏡方案



向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI