文章收录:

  • 集体网址: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事件,并获取鼠标左键按下时clientXclientY的值作为开始拉伸的地位,默认为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源码登程,本人也对图像拉伸的性能实现了一遍,做到脑会手也会。当前再有相似的性能分分钟搞定它!

关注张鑫旭大佬,好处多多。

如果我的文章对你有帮忙,你的就是对我的最大反对^_^。