文章收录:
- 集体网址:http://linglan01.cn/
- Github仓库:https://github.com/CatsAndMice/blog/issues
产品经理又有新需要啦,其中有一个图片上传后用户拉伸图像宽高的性能,评估后因要卡上线工夫来不及砍掉了。保不准下一个版本又会提这个性能,所以还是要去钻研钻研。
幸好我有关注张鑫旭大佬的博客,印象中记得发表过一篇对于图像拉伸的文章,就是它JS之我用单img元素实现了图像resize拉伸成果。刚好满足产品想要的成果,demo都是现成的。
文章对js逻辑局部并没有形容,像我这种爱学习,那不得知其所以然。
因而,我读了读源码200行左右,并且去掉边界判断逻辑,只将外围逻辑写了一遍。
先把成果秀进去:
先搞定图像拉伸款式
先写一个img元素,给它的src属性增加一个在线的图像链接。
<img class="image-size" src="https://pic1.zhimg.com/v2-d58ce10bf4e01f5086c604a9cfed29f3_r.jpg?source=1940ef5c" alt="拉伸">
再给它整点款式重点是 border-image
属性,大佬文章也是介绍应用border-image
属性做到单img实现拉伸。不赘述,跟我一样爱学习的人必定会去瞅一眼大佬文章的。
/* 先默认宽度400px */
.image-size {
width: 400px;
}
img.active {
cursor: default;
z-index: 1;
display: inline-block;
vertical-align: bottom;
font-size: 12px;
border: 3px solid transparent;
border-image: url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='%23914AFF' d='M2.5 2.5h25v25h-25z'/%3E%3Cpath d='M0 0v12h2V2h10V0H0zM0 30V18h2v10h10v2H0zM30 0H18v2h10v10h2V0zM30 30H18v-2h10V18h2v12z' fill='%23914AFF'/%3E%3C/svg%3E") 12 / 12px / 0;
margin: -1px;
position: relative;
-webkit-user-select: none;
user-select: none;
}
点击图片后,给它增加一个active
类名。若点击的不是该图像,则革除图像的active
类名。
const image = document.getElementsByClassName('image-size')[0]
image.onclick = (e) => {
if (image.classList.contains('active')) return
image.classList.add('active')
}
document.onclick = (e) => {
if (e.target === image) return
image.classList.remove('active')
}
如下GIF录屏所示:
也能够点击这里体验:拉伸款式demo
再搞定鼠标光标款式
鼠标光标默认箭头,当初须要当鼠标挪动到图像左上、左下、右上、右下四个角时,鼠标光标款式随之进行扭转。
留神看GIF演示中的鼠标变动:
上图是大佬的demo。
鼠标挪动至左上角、右下角处,鼠标光标款式批改成:
鼠标挪动至左下角、右上角,鼠标光标款式批改成:
批改鼠标光标应用属性cursor
,对该属性不分明的童鞋们移步cursor官网文档。
给图像增加active
类名后,再给document
绑定一个鼠标挪动事件onmousemove
。当鼠标挪动过程中计算鼠标地位是否已进行某个区域内。
该区域能够为下图红框框起来的区域,图像左上、左下、右上、右下四个角均会有一个这样的区域。
计算过程要获取到图像的left、top、right、bottom
值,也就是应用Element.getBoundingClientRect()
,不分明该API的童鞋移步getBoundingClinetRect官网文档
//省略...
image.onclick = (e) => {
if (image.classList.contains('active')) return
image.classList.add('active')
document.onmousemove = (e) => {
const target = e.target
if (target !== image || !target.classList.contains('active')) return
const x = e.clientX,
y = e.clientY
const { top, left, bottom, right } = image.getBoundingClientRect()
//左上角或右下角
if ((bottom - y < 20 && right - x < 20) || (x - left < 20 && y - top < 20)) {
image.style.cursor = 'nwse-resize'
//左下角或右上角
} else if ((y - top < 20 && right - x < 20) || (bottom - y < 20 && x - left < 20)) {
image.style.cursor = 'nesw-resize'
//若都不是,鼠标光标为默认箭头款式
} else {
image.style.cursor = 'default'
}
}
}
//省略...
伪代码中20
这个数字是我随便写的,这个值为红框框起来区域的宽高。
若勾销图像的拉伸状态,则也把document
已绑定的鼠标挪动事件勾销。
//省略...
document.onclick = (e) => {
if (e.target === image) return
image.classList.remove('active')
document.onmousemove = null
}
能够点击这里体验:图像拉伸鼠标款式扭转demo。
让图像宽高动起来
先给image元素增加onmousedown
事件,并获取鼠标左键按下时clientX
、clientY
的值作为开始拉伸的地位,默认为0
拉伸逻辑计算产生在document.onmousemove
事件内,因而,须要有一个变量isResizeing
示意image.onmousedown
事件是否被触发。触发,鼠标挪动即图像宽高也要进行扭转;未触发,鼠标挪动仅扭转鼠标光标的款式,不影响图像的宽高。
isResizeing
不能始终为true
,鼠标抬起onmouseup
重置为false
。
//省略...
image.onclick = (e) => {
if (image.classList.contains('active')) return
image.classList.add('active')
let startX = 0,
startY = 0,
//是否拉伸状态
isResizeing = false,
position = ''
image.onmousedown = (e) => {
//鼠标左键落下的X、Y地位
startX = e.clientX
startY = e.clientY
isResizeing = true
}
document.onmousemove = (e) => {
e.preventDefault()
const target = e.target
let x = e.clientX,
y = e.clientY,
distanceX = x - startX,
distanceY = y - startY
if(isResizeing){
//图像已处于拉伸的状态
}else{
//扭转鼠标光标款式
if(if (target !== image || !target.classList.contains('active')) return){
const { top, left, bottom, right } = image.getBoundingClientRect()
//左上角或右下角
if ((bottom - y < 20 && right - x < 20) || (x - left < 20 && y - top < 20)) {
image.style.cursor = 'nwse-resize'
//左下角或右上角
} else if ((y - top < 20 && right - x < 20) || (bottom - y < 20 && x - left < 20)) {
image.style.cursor = 'nesw-resize'
//若都不是,鼠标光标为默认箭头款式
} else {
image.style.cursor = 'default'
}
}
}
document.onmouseup = () => {
//鼠标抬起时,重置isResizeing变量
isResizeing = false
}
}
//省略...
还须要有一个变量 position
,用于判断拉伸的是左上、右下、左下、右上四个角中的哪一个 。
//省略
if ((bottom - y < 20 && right - x < 20) || (x - left < 20 && y - top < 20)) {
//左上角或右下角
image.style.cursor = 'nwse-resize'
// 右下角
if (bottom - y < 20) {
position = 'bottom right'
return
}
position = 'top left'
} else if ((y - top < 20 && right - x < 20) || (bottom - y < 20 && x - left < 20)) {
//左下角或右上角
image.style.cursor = 'nesw-resize'
// 右上角
if (y - top < 20) {
position = 'top right'
return
}
position = 'bottom left'
//若都不是,鼠标光标为默认箭头款式
} else {
image.style.cursor = 'default'
position = ''
}
//省略 ...
有了position
变量,即可在if(isResizeing){...}
这个代码块中根据position
值进行拉伸计算。
//省略...
image.onclick = (e) => {
if (image.classList.contains('active')) return
image.classList.add('active')
let startX = 0,
startY = 0,
//是否拉伸状态
isResizeing = false,
position = '',
storeWidth = 0,
storeHeight = 0
image.onmousedown = (e) => {
//鼠标左键落下的X、Y地位
startX = e.clientX
startY = e.clientY
isResizeing = true
storeWidth = image.clientWidth
storeHeight = image.clientHeight
}
document.onmousemove = (e) => {
e.preventDefault()
const target = e.target
let x = e.clientX,
y = e.clientY,
distanceX = x - startX,
distanceY = y - startY,
width = 0,
height = 0
if(isResizeing){
//图像已处于拉伸的状态
if (!position) return
if (position === 'bottom right') {
/*
右下角
distanceX值为正,distanceY值为正,图像宽高变大;
distanceX值为负,distanceY值为负,图像宽高变小
*/
width = storeWidth + distanceX
height = storeHeight + distanceY
} else if (position === 'top left') {
/*
左上角正好与右下角相同
distanceX值为正,distanceY值为正,图像宽高变大;
distanceX值为负,distanceY值为负,图像宽高变小
*/
width = storeWidth - distanceX
height = storeHeight - distanceY
} else if (position === 'top right') {
/*
右上角
distanceX值为正,distanceY值为负,图像宽高变大;
distanceX值为负,distanceY值为正,图像宽高变小
*/
width = storeWidth + distanceX
height = storeHeight - distanceY
} else if (position === 'bottom left') {
/*
左下角
distanceX值为负,distanceY值为正,图像宽高变大;
distanceX值为正,distanceY值为负,图像宽高变小
*/
width = storeWidth - distanceX
height = storeHeight + distanceY
}
image.style.width = Math.round(width) + 'px'
image.style.height = Math.round(height) + 'px'
}else{
//省略
}
document.onmouseup = () => {
//鼠标抬起时,重置isResizeing变量
isResizeing = false
}
}
//省略...
该局部代码能够优化,但这样写容易了解,如何优化能够看下张鑫旭/单IMG元素的图像拉伸成果 。
实现这步,图像曾经能够位伸宽高了。
如下GIF录屏所示:
能够点击这里体验:图像拉伸宽高扭转。
图像是有比例的,如常见比例16:9,3:4等。当初实现进去的拉伸没有按比例进行拉伸,还须要优化。
先计算出图像比例,比例=图像宽度/ 图像高度。比拟distanceX、distanceY
两者值谁的挪动间隔更大。若distanceX
值更大,则以图像宽度计算出它的高度;若distanceY
值更大,则以图像的高度计算出它的宽度。
//省略...
image.onclick = (e) => {
//省略
document.onmousemove = (e) => {
e.preventDefault()
const target = e.target
let x = e.clientX,
y = e.clientY,
distanceX = x - startX,
distanceY = y - startY,
width = 0,
height = 0
if(isResizeing){
//图像已处于拉伸的状态
if (!position) return
//省略
let imageWidth = 0,
imageHeight = 0
const ratio = storeWidth / storeHeight
// 抉择挪动间隔大的方向
if (Math.abs(distanceX) > Math.abs(distanceY)) {
// 宽度变动为主
imageWidth = width;
imageHeight = width / ratio;
} else {
// 高度变动为主
imageHeight = height;
imageWidth = height * ratio;
}
image.style.width = Math.round(imageWidth) + 'px'
image.style.height = Math.round(imageWidth) + 'px'
}else{
//省略
}
//省略
}
//省略...
OK,到此结束。
如下GIF录屏所示:
能够点击这里体验:图像拉伸。
总结
从读张鑫旭的文章demo源码登程,本人也对图像拉伸的性能实现了一遍,做到脑会手也会。当前再有相似的性能分分钟搞定它!
关注张鑫旭大佬,好处多多。
如果我的文章对你有帮忙,你的👍就是对我的最大反对^_^。
发表回复