openlayer中实现截图框截图的功能

实现

接着上篇文章写(https://blog.csdn.net/luoluoyang23/article/details/122653363),这次会完整的实现截图的功能
首先要放个地图,直接参照https://openlayers.org/en/latest/doc/quickstart.html写就行了

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/css/ol.css" type="text/css">
  <title>Document</title>
  <style type="text/css">
    html, body {
      margin: 0;
      padding: 0;
    }
    #screenshot {
      border: 2px solid black;
    }
    .map {
      width: 100%;
      height: 600px;
    }
  </style>
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/build/ol.js"></script>
</head>
<body>
  <div id="map" class="map"></div>
</body>
<script>
  const map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      })
    ],
    view: new ol.View({
      center: ol.proj.fromLonLat([37.41, 8.82]),
      zoom: 4
    })
  })
  window.addEventListener("mousedown", (e) => {
    const [startX, startY] = [e.clientX, e.clientY]
    const divDom = document.createElement("div")
    divDom.id = 'screenshot'
    divDom.width = '1px'
    divDom.height = '1px'
    divDom.style.position = "absolute"
    divDom.style.top = startY + "px"
    divDom.style.left = startX + "px"
    document.body.appendChild(divDom)
    const moveEvent = (e) => {
      const moveX = e.clientX - startX
      const moveY = e.clientY - startY
      if (moveX > 0) {
        divDom.style.width = moveX + 'px'
      } else {
        divDom.style.width = -moveX + 'px'
        divDom.style.left = e.clientX + 'px'
      }
      if (moveY > 0) {
        divDom.style.height = moveY + 'px'
      } else {
        divDom.style.height = -moveY + 'px'
        divDom.style.top = e.clientY + 'px'
      }
    }
    window.addEventListener("mousemove", moveEvent)
    window.addEventListener("mouseup", () => {
      window.removeEventListener("mousemove", moveEvent)
    })
  })
</script>
</html>

我这里代码是直接在上次的代码基础上写的,看看效果
在这里插入图片描述
改点东西,因为我们截图事件是直接写在windows上面的,这样点地图也会触发,随便加个按钮,把事件挪过去

<button id="screen-button">截图</button>
...
document.getElementById('screen-button').addEventListener('click', (e) => {
    const mousedownEvent = (e) => {
      const [startX, startY] = [e.clientX, e.clientY]
      const divDom = document.createElement("div")
      divDom.id = 'screenshot'
      divDom.width = '1px'
      divDom.height = '1px'
      divDom.style.position = "absolute"
      divDom.style.top = startY + "px"
      divDom.style.left = startX + "px"
      document.body.appendChild(divDom)
      const moveEvent = (e) => {
        const moveX = e.clientX - startX
        const moveY = e.clientY - startY
        if (moveX > 0) {
          divDom.style.width = moveX + 'px'
        } else {
          divDom.style.width = -moveX + 'px'
          divDom.style.left = e.clientX + 'px'
        }
        if (moveY > 0) {
          divDom.style.height = moveY + 'px'
        } else {
          divDom.style.height = -moveY + 'px'
          divDom.style.top = e.clientY + 'px'
        }
      }
      window.addEventListener("mousemove", moveEvent)
      window.addEventListener("mouseup", () => {
        window.removeEventListener("mousemove", moveEvent)
        window.removeEventListener("mousedown", mousedownEvent)
      })
    }
    window.addEventListener("mousedown", mousedownEvent)
  })

这里直接移进去了,这样点击按钮后才会把截图事件绑定到window上,在鼠标松开时移除这些事件
现在我们在网页中测试能够发现,当我们截图时鼠标移动也会拖动地图移动,这样没办法很好的截图,解决这个问题有很多思路,可以在初始化地图的时候拿到拖动地图的事件进行控制,我之前在写地图色块提取的时候有用过透明遮罩的办法,自己权衡。这里采用前一种,参考:

https://blog.csdn.net/u013323965/article/details/53183532

let pan
map.getInteractions().forEach(function(element,index,array){
	if(element instanceof ol.interaction.DragPan) pan = element; 
})

在事件开始和结尾处加上相应的代码即可
在这里插入图片描述
在这里插入图片描述
然后就是获取图片了,在上一篇文章我已经给过相关API的链接地址,可以自己去研究,这里直接贴代码了

function getMapImg(startX, startY, mWidth, mHeight) {
    map.once('rendercomplete', () => {
      const mapCanvas = document.createElement('canvas')
      mapCanvas.width = mWidth
      mapCanvas.height = mHeight
      const mapContext = mapCanvas.getContext('2d')
      Array.prototype.forEach.call(
        document.querySelectorAll('.ol-layer canvas'),
        function (canvas) {
          if (canvas.width > 0) {
            const opacity = canvas.parentNode.style.opacity
            mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity)
            const transform = canvas.style.transform
            // Get the transform parameters from the style's transform matrix
            const matrix = transform
              .match(/^matrix\(([^(]*)\)$/)[1]
              .split(',')
              .map(Number)
            // Apply the transform to the export map context
            CanvasRenderingContext2D.prototype.setTransform.apply(
              mapContext,
              matrix
            )
            mapContext.drawImage(canvas, -startX, -startY)
          }
        }
      )
      if (navigator.msSaveBlob) {
        // link download attribute does not work on MS browsers
        navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png')
      } else {
        dataUrl = mapCanvas.toDataURL()
        console.log(dataUrl)
      }
    })
    map.renderSync()
  }

四个参数,分别是截图范围的起点xy坐标,截图框的宽高。这就是我们写截图框代码的目的。这四个参数就从上面拿就行了,这里也直接写在之前的代码中获取,为了大家方便查看,这里直接贴出整体的代码

let [canvasX, canvasY] = [startX, startY]
let canvasWidth, canvasHeight
divDom.style.top = startY + "px"
divDom.style.left = startX + "px"
document.body.appendChild(divDom)
const moveEvent = (e) => {
  const moveX = e.clientX - startX
  const moveY = e.clientY - startY
  if (moveX > 0) {
    divDom.style.width = moveX + 'px'
    canvasWidth = moveX
  } else {
    divDom.style.width = -moveX + 'px'
    divDom.style.left = e.clientX + 'px'
    canvasWidth = -moveX
    canvasX = e.clientX
  }
  if (moveY > 0) {
    divDom.style.height = moveY + 'px'
    canvasHeight = moveY
  } else {
    divDom.style.height = -moveY + 'px'
    divDom.style.top = e.clientY + 'px'
    canvasHeight = -moveY
    canvasY = e.clientY
  }
}

在截图结束的地方调用getMapImg()

window.addEventListener("mouseup", () => {
  window.removeEventListener("mousemove", moveEvent)
  window.removeEventListener("mousedown", mousedownEvent)
  pan.setActive(true)
  getMapImg(canvasX, canvasY, canvasWidth, canvasHeight)
})

在getMapImg中最后几行的dataUrl就是图片地址,可以直接在浏览器中打开,现在我们测试效果
在这里插入图片描述
在这里插入图片描述
可以看到我们成功实现了效果,接下来就是一些优化截图表现效果的代码了。具体效果可以按照自己喜欢的来,我这里走比较常规的套路,在截图结束时清除截图框,在窗口中间增加一个预览截图的效果
在这里插入图片描述
这部分写得比较随意,下面时完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/css/ol.css" type="text/css">
  <title>Document</title>
  <style type="text/css">
    html, body {
      margin: 0;
      padding: 0;
    }
    #screenshot {
      border: 3px solid black;
    }
    .map {
      width: 100%;
      height: 600px;
    }
    #photo-window {
      position: absolute;
      border: 25px solid rgba(0, 0, 0, 0.5);
      overflow: hidden;
    }
    .hide {
      display: none;
    }
  </style>
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/build/ol.js"></script>
</head>
<body>
  <div id="map" class="map"></div>
  <button id="screen-button">截图</button>
  <div id="photo-window" class="hide">
    <img id="photo" src="" alt="#" />
  </div>
</body>
<script>
  const map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      })
    ],
    view: new ol.View({
      center: ol.proj.fromLonLat([37.41, 8.82]),
      zoom: 4
    })
  })
  let dataUrl
  let pan
  map.getInteractions().forEach(function(element,index,array){
		if(element instanceof ol.interaction.DragPan) pan = element; 
	})
  document.getElementById('screen-button').addEventListener('click', (e) => {
    const mousedownEvent = (e) => {
      pan.setActive(false)
      const [startX, startY] = [e.clientX, e.clientY]
      const divDom = document.createElement("div")
      divDom.id = 'screenshot'
      divDom.width = '1px'
      divDom.height = '1px'
      divDom.style.position = "absolute"
      let [canvasX, canvasY] = [startX, startY]
      let canvasWidth, canvasHeight
      divDom.style.top = startY + "px"
      divDom.style.left = startX + "px"
      document.body.appendChild(divDom)
      const moveEvent = (e) => {
        const moveX = e.clientX - startX
        const moveY = e.clientY - startY
        if (moveX > 0) {
          divDom.style.width = moveX + 'px'
          canvasWidth = moveX
        } else {
          divDom.style.width = -moveX + 'px'
          divDom.style.left = e.clientX + 'px'
          canvasWidth = -moveX
          canvasX = e.clientX
        }
        if (moveY > 0) {
          divDom.style.height = moveY + 'px'
          canvasHeight = moveY
        } else {
          divDom.style.height = -moveY + 'px'
          divDom.style.top = e.clientY + 'px'
          canvasHeight = -moveY
          canvasY = e.clientY
        }
      }
      window.addEventListener("mousemove", moveEvent)
      window.addEventListener("mouseup", () => {
        window.removeEventListener("mousemove", moveEvent)
        window.removeEventListener("mousedown", mousedownEvent)
        pan.setActive(true)
        getMapImg(canvasX, canvasY, canvasWidth, canvasHeight)
        document.body.removeChild(divDom)
        generateWindow()
      })
    }
    window.addEventListener("mousedown", mousedownEvent)
  })

  function getMapImg(startX, startY, mWidth, mHeight) {
    map.once('rendercomplete', () => {
      const mapCanvas = document.createElement('canvas')
      mapCanvas.width = mWidth
      mapCanvas.height = mHeight
      const mapContext = mapCanvas.getContext('2d')
      Array.prototype.forEach.call(
        document.querySelectorAll('.ol-layer canvas'),
        function (canvas) {
          if (canvas.width > 0) {
            const opacity = canvas.parentNode.style.opacity
            mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity)
            const transform = canvas.style.transform
            // Get the transform parameters from the style's transform matrix
            const matrix = transform
              .match(/^matrix\(([^(]*)\)$/)[1]
              .split(',')
              .map(Number)
            // Apply the transform to the export map context
            CanvasRenderingContext2D.prototype.setTransform.apply(
              mapContext,
              matrix
            )
            mapContext.drawImage(canvas, -startX, -startY)
          }
        }
      )
      if (navigator.msSaveBlob) {
        // link download attribute does not work on MS browsers
        navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png')
      } else {
        dataUrl = mapCanvas.toDataURL()
        generateWindow(mWidth, mHeight)
      }
    })
    map.renderSync()
  }

  function generateWindow(width, height) {
    document.getElementById('photo').src = dataUrl
    const boxDom = document.getElementById('photo-window')
    boxDom.classList.remove('hide')
    boxDom.style.left = 'calc(50% - ' + (width / 2) + 'px)'
    boxDom.style.top = 'calc(50% - ' + (height / 2) + 'px)'
  }
</script>
</html>

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值