首先在淘宝上找一个宝贝链接,打开控制台,看一下淘宝上的放大镜功能的结构是怎样实现的
不难看出来,主图区域功能最外面一层是div用来存放主要功能区的,position: relative;
,主图并没有定位,只是一个img标签,滑块区域和放大图区域都是相对于外侧父元素定位,附图利用background-position
来改变位置,其次主图和放大图下载下来后,尺寸属性是一样的,都是750 * 1000
,那么首先在html中搭建这样的一个结构。
html结构
在vscode中输入main>(div.product-img>img+div.zoom+div.tools)+div.product-info
回车,则会生成上述的结构,手动添加一个图片链接,为了防止之后链接失效,
html <main> <div class="product-img"> <img src="" alt="商品主图"> <div class="zoom"></div> <div class="tools"></div> </div> <div class="product-info"></div> </main>
样式
根据结构,写less设置一下各个dom节点的样式
```less main{ display:flex; height: 450px; margin-right: 18px; width: 100%; .product-img { position: relative; height: 100%; width: 450px; background-color: #efefef; img { display:block; height: 100%; // 按照父元素尺寸等比缩放 width: calc((450 / 1000) * 750px); margin: auto; } .zoom { position: absolute; top: 0; right: -20px; transform: translateX(100%); background-image: url(../assets/image/product.png); width: 388px; height: 100%; // 固定图片值,可动态获取 background-size: 750px 1000px; border-radius: 10px; overflow: hidden; z-index: 2; } .tools { width: 128px; height: 128px; background: rgba(129, 167, 249, 0.485); position: absolute; left: 0; top: 0; z-index: 2; } } .product-info { flex: 4; background: #9f9f9f; }
} ```
加入样式的布局
放大逻辑
.tools滑块移动
分析一下滑块移动的范围,超过主图的范围禁止超越,超过主图外侧dom节点的位置滑块隐藏,前期为了方便观察,先不做隐藏功能,只做移动和范围限制。
这四条蓝色的线,就是滑块可移动的最大范围
```typescript class Vector2 { x: number; y: number; constructor(x = 0, y = 0) { this.x = x; this.y = y } // 相加 add(v2: Vector2) { return new Vector2(v2.x + this.x, v2.y + this.y) } // 相减 sub(v2: Vector2) { return new Vector2(this.x - v2.x, this.y - v2.y) } divided(n: number) { return new Vector2(this.x / n, this.y / n) } }
// 获取图片相关尺寸 const img = document.querySelector('img') const width = img?.offsetWidth const height = img?.offsetHeight const imgSize = new Vector2(width, height)
// 图片左上角距离父元素的位置 const imgLeft = img?.offsetLeft const imgTop = img?.offsetTop const LT = new Vector2(imgLeft, imgTop) // 图片右下角位置 const RB = LT.add(imgSize)
const imgParent = document.querySelector('.product-img') as any; const parentPos = new Vector2(imgParent.offsetLeft, imgParent.offsetTop)
// zoom图层信息 const zoom = document.querySelector('.zoom') as HTMLDivElement; const zoomSize = new Vector2(zoom.offsetWidth, zoom.offsetHeight)
// 移动模块相关参数 const tools = document.querySelector('.tools') as HTMLDivElement; const toolsLeft = tools.offsetLeft; const toolsTop = tools.offsetTop;
// 根据zoom图层跟大图尺寸的比例计算出tools的尺寸 // 犯懒了,750和1000这两个数值应该从图片上获取 const toolsWidth = zoomSize.x / 750 * imgSize.x const toolsHeight = zoomSize.y / 1000 * imgSize.y tools.style.width = toolsWidth + 'px' tools.style.height = toolsHeight + 'px' const toolsSize = new Vector2(toolsWidth, toolsHeight)
// 鼠标位置 const mouseMove = (e: any) => { // 获取当前鼠标位置 const mouse = new Vector2(e?.clientX, e?.clientY); // console.dir(mouse); // 鼠标当前位置-父元素距离页面位置-模块尺寸的一半 const toolsPosition = mouse.sub(parentPos).sub(toolsSize.divided(2)) tools.style.left = toolsPosition.x + 'px' tools.style.top = toolsPosition.y + 'px' }
if (imgParent) { imgParent.onmousemove = mouseMove }
```
以上为移动模块的功能,效果如下
关于Vector2类是为了方便各个坐标之间的计算,不使用也可以,只要能计算出来就行,
限制范围
新增一个限制方法,传入当前模块位置,进行限制后,返回可在位置的值
```typescript // 图片右下角限制 const RBRange = RB.sub(toolsSize) // 限制范围 const limitRange = (pos: Vector2): Vector2 => { let { x, y } = pos
// 先判断左上角
x = x >= LT.x ? x : LT.x
y = y >= LT.y ? y : LT.y
// 再判断右下角
x = x <= RBRange.x ? x : RBRange.x
y = y <= RBRange.y ? y : RBRange.y
return new Vector2(x, y)
} ```
在mousemove方法中使用这个限制方法
typescript ... const pos = limitRange(toolsPosition) tools.style.left = pos.x + 'px' tools.style.top = pos.y + 'px' ...
限制范围后的效果
放大zoom图
按照上图的比例计算公式,带入到getScale
方法中,改变.zoom元素的backgroundPosition
属性
```typescript const getScale = () => { // 改变左右位置 const left = tools.offsetLeft - (img?.offsetLeft || 0) const scaleX = left / imgSize.x const x = scaleX * zoomBackgroundImageWidth zoom.style.backgroundPositionX = -x + 'px'
// 改变上下位置
const top = tools.offsetTop - (img?.offsetTop || 0)
const scaleY = top / imgSize.y
const y = scaleY * zoomBackgroundImageHeight
zoom.style.backgroundPositionY = -y + 'px'
} ```
zoomBackgroundImageWidth
这个尺寸是大图的原本的尺寸,前面写的固定值,按理说这个值应该动态获取。
细节调整
接下来剩的工作就相对简单一点,根据不同时机,将元素显示隐藏即可