当前位置: 首页> 财经> 访谈 > 深圳有限公司官网_西安凡高网络_网站怎么营销推广_竞价托管是啥意思

深圳有限公司官网_西安凡高网络_网站怎么营销推广_竞价托管是啥意思

时间:2025/7/11 7:52:31来源:https://blog.csdn.net/nndsb/article/details/145554577 浏览次数:0次
深圳有限公司官网_西安凡高网络_网站怎么营销推广_竞价托管是啥意思

快速拖动出现问题,慢慢拖动不会有问题”的现象,问题的根本原因可能是鼠标移动事件触发得太频繁,导致每次鼠标移动时都重新渲染瓦片,造成性能问题或者视觉上的不流畅。特别是在快速拖动时,浏览器需要处理大量的瓦片加载和渲染,这就容易出现卡顿。

先上问题代码,快速拖动出现问题,慢慢拖动不会有问题

快速拖动出现问题,慢慢拖动不会有问题
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>瓦片拖动功能</title><style>
/*        地图容器*/.map {position: fixed;width: 100%;height: 100%;left: 0;top: 0;}/*十字架*/.line {position: absolute;background-color: #000;}/*十字架横轴*/.lineX {left: 50%;top: 50%;transform: translate(-50%, -50%);width: 50px;/*改成50px*/height: 2px;}/*十字架纵轴*/.lineY {left: 50%;top: 50%;transform: translate(-50%, -50%);height: 50px;/*改成50px*/width: 2px;}.clearButton{position: absolute;left: 10px;top: 10px;cursor: pointer;}.refreshButton{position: absolute;left: 90px;top: 10px;cursor: pointer;}</style>
</head>
<body><div class="map" id="map">
<!--        画布--><canvas id="canvas"  onmousedown="onMousedown(event)"></canvas>
<!--        十字架横轴--><div class="line lineX"></div>
<!--        十字架纵轴--><div class="line lineY"></div><button class="clearButton" onclick="renderTiles()">重绘画布</button><button class="refreshButton" onclick="onClear()">清除画布</button></div>
</body><script>// 角度转弧度
function angleToRad(angle){return angle * (Math.PI / 180)
}// 弧度转角度
function radToAngle(rad){return rad * (180 / Math.PI)
}// 地球半径  单位:米
let EARTH_RAD = 6378137// 4326转3857
function lngLatToMercator(lng, lat){// 经度先转弧度,然后因为 弧度 = 弧长 / 半径 ,得到弧长为 弧长 = 弧度 * 半径let x = angleToRad(lng) * EARTH_RAD// 纬度先转弧度let rad = angleToRad(lat)// 下面我就看不懂了let sin = Math.sin(rad)let y = (EARTH_RAD / 2) * Math.log((1 + sin) / (1 - sin))return [x, y]
}// 3857转4326
function mercatorToLngLat(x, y){let lng = radToAngle(x) / EARTH_RADlet lat = radToAngle(2 * Math.atan(Math.exp(y / EARTH_RAD)) - Math.PI / 2)return [lng, lat]
}// 地球周长
let EARTH_PERIMETER = 2 * Math.PI * EARTH_RAD
// 瓦片像素
let TILE_SIZE = 256// 获取某一层级下的分辨率     n 从0层开始
function getResolution(n){let tileNums = Math.pow(2, n)               //一行的瓦片数量console.log("n,tileNums",n,tileNums)let tileTotalPx = tileNums * TILE_SIZE         //一行的瓦片像素点return EARTH_PERIMETER / tileTotalPx           // 每像素点代表多少米
}// 拼接瓦片地址
function getTileUrl(x, y, z){let domainIndexList = [1, 2, 3, 4]let domainIndex =domainIndexList[Math.floor(Math.random() * domainIndexList.length)]return `https://webrd0${domainIndex}.is.autonavi.com/appmaptile?x=${x}&y=${y}&z=${z}&lang=zh_cn&size=1&scale=1&style=8`
}// 根据3857坐标及缩放层级计算瓦片行列号function getTileRowAndCol1 (x, y, z){let resolution = getResolution(z)let row = Math.floor(x / resolution / TILE_SIZE)let col = Math.floor(y / resolution / TILE_SIZE)return [row, col]}function getTileRowAndCol2 (x, y, z){x += EARTH_PERIMETER / 2     // ++y = EARTH_PERIMETER / 2 - y  // ++let resolution = getResolution(z)let row = Math.floor(x / resolution / TILE_SIZE)let col = Math.floor(y / resolution / TILE_SIZE)return [row, col]}// 分辨率列表let resolutions = []for (let i = 0; i <= 18; i++) {resolutions.push(getResolution(i))}// 计算4326经纬度对应的像素坐标function getPxFromLngLat (lng, lat, z) {let [_x, _y] = lngLatToMercator(lng, lat)// 4326转3857// 转成世界平面图的坐标_x += EARTH_PERIMETER / 2_y = EARTH_PERIMETER / 2 - _ylet resolution = resolutions[z]// 该层级的分辨率// 米/分辨率得到像素let x = Math.floor(_x / resolution)let y = Math.floor(_y / resolution)return [x, y]}</script><script>let tileCache={}//缓存瓦片let currentTileCache={} //记录当前地图上的瓦片let center=[120.148732,30.231006]    //已知变量,中心坐标   雷峰塔坐标let zoom=17    //已知变量, 缩放层级// 容器大小let map=document.getElementById('map')let canvas=document.getElementById('canvas')let { width, height } = map.getBoundingClientRect()let isMousedown= false//是否按下鼠标// 设置画布大小canvas.width = widthcanvas.height = height// 获取绘图上下文let ctx = canvas.getContext('2d')// 移动画布原点到画布中间,本来画布原点是左上角的ctx.translate(width / 2, height / 2)renderTiles()//     绘制function renderTiles(){console.log("center=============",center)// 中心点对应的瓦片let centerTile = getTileRowAndCol2(...lngLatToMercator(...center),// 4326转3857zoom                            // 缩放层级)// 中心瓦片左上角对应的像素坐标let centerTilePos = [centerTile[0] * TILE_SIZE,centerTile[1] * TILE_SIZE,];// 中心点对应的像素坐标let centerPos = getPxFromLngLat(...center, zoom)// 中心像素坐标距中心瓦片左上角的差值let offset = [centerPos[0] - centerTilePos[0],centerPos[1] - centerTilePos[1]]// 计算瓦片数量    Math.ceil()向上取整let rowMinNum = Math.ceil((width / 2 - offset[0]) / TILE_SIZE)// 左let colMinNum = Math.ceil((height / 2 - offset[1]) / TILE_SIZE)// 上let rowMaxNum = Math.ceil((width / 2 - (TILE_SIZE - offset[0])) / TILE_SIZE)// 右let colMaxNum = Math.ceil((height / 2 - (TILE_SIZE - offset[1])) / TILE_SIZE)// 下// 渲染画布内所有瓦片currentTileCache = {}; // 清空缓存对象// 从上到下,从左到右,加载瓦片for (let i = -rowMinNum; i <= rowMaxNum; i++) {for (let j = -colMinNum; j <= colMaxNum; j++) {// 当前瓦片的行列号let row = centerTile[0] + ilet col = centerTile[1] + j// 当前瓦片的显示位置let x = i * TILE_SIZE - offset[0]let y = j * TILE_SIZE - offset[1]// 缓存keylet cacheKey = row + '_' + col + '_' + zoom// 记录画布当前需要的瓦片currentTileCache[cacheKey] = trueif (tileCache[cacheKey]) {// 更新到当前位置if(currentTileCache[cacheKey]){ctx.drawImage(tileCache[cacheKey],x,y)}}else {// 加载瓦片图片let img = new Image()img.src = getTileUrl(row,// 行号col,// 列号zoom)tileCache[cacheKey]=imgimg.onload = () => {// 只有在当前画布上的瓦片才需要渲染if( currentTileCache[cacheKey]){// 渲染到canvasctx.drawImage(img,x,y)}}}}}}function render(ctx,img,x,y,cacheKey){if (!currentTileCache[cacheKey]) {return}ctx.drawImage(img, x, y)}// 鼠标按下function onMousedown(e){if (e.which === 1) {isMousedown = true;}}window.addEventListener("mousemove", onMousemove);window.addEventListener("mouseup", onMouseup);// 鼠标移动function onMousemove(e){if (!isMousedown) {return;}// 计算本次拖动的距离对应的经纬度数据let mx = e.movementX * resolutions[zoom];//表示这一次鼠标事件距离上一次鼠标事件横向上的偏移量   ,基本就是 1 像素let my = e.movementY * resolutions[zoom];
// 把当前中心点经纬度转成3857坐标let [x, y] = lngLatToMercator(...center);
// 更新拖动后的中心点经纬度center = mercatorToLngLat(x - mx, my + y);onClear()renderTiles()}// 鼠标松开function onMouseup(){isMousedown = false;}// 清除画布function onClear(){ctx.clearRect(-width / 2,   //参数1 和2 是起始位置-height / 2,width,height);}</script></html>     

建议可以通过以下几个方法来优化:

1. 防抖(Debounce)鼠标移动事件

防抖的目的是限制事件的触发频率,避免每次快速拖动都调用渲染函数,导致过多的绘制操作。可以使用 setTimeout 来做一个简单的防抖。

例如,你可以这样修改 onMousemove 函数:

let debounceTimer;function onMousemove(e) {if (!isMousedown) {return;}// 清除上一次的定时器clearTimeout(debounceTimer);// 设置一个新的定时器,延迟50ms执行渲染操作debounceTimer = setTimeout(() => {let mx = e.movementX * resolutions[zoom];let my = e.movementY * resolutions[zoom];// 转换当前经纬度let [x, y] = lngLatToMercator(...center);center = mercatorToLngLat(x - mx, my + y);onClear();renderTiles();}, 50); // 可以调整50ms的延迟
}

2. 避免重复渲染

你可以检查一下是否已经绘制了某个瓦片,而不需要每次都重新绘制。例如,如果某个瓦片已经加载并显示在画布上,且位置没有变化,就不需要重新绘制它。

你可以在 renderTiles 函数中优化一下瓦片的绘制逻辑,减少不必要的绘制。

3. 限制瓦片加载并发

当快速拖动时,浏览器可能会同时请求大量的瓦片,导致性能瓶颈。你可以通过限制并发请求的数量来减少浏览器的负担。例如,可以在加载瓦片时加入一个阈值,防止一次性加载过多瓦片。

比如,你可以在 renderTiles 中加入判断,只有当瓦片超出缓存时才进行加载,避免重复加载已显示的瓦片。

4. 使用 requestAnimationFrame

requestAnimationFrame 是一种优化动画渲染的方式,它确保浏览器在下一帧绘制时才进行渲染,从而减少了不必要的重绘,提升了性能。你可以使用 requestAnimationFrame 来包裹渲染函数,这样就能确保只会在浏览器刷新时执行一次渲染操作。

比如:

let isRendering = false;function onMousemove(e) {if (!isMousedown) {return;}if (!isRendering) {isRendering = true;requestAnimationFrame(() => {let mx = e.movementX * resolutions[zoom];let my = e.movementY * resolutions[zoom];let [x, y] = lngLatToMercator(...center);center = mercatorToLngLat(x - mx, my + y);onClear();renderTiles();isRendering = false;});}
}

这样,每次 mousemove 事件都会排队等待浏览器下一帧来执行,这样就不会造成频繁的渲染。

通过使用防抖技术(Debounce)、优化瓦片加载与渲染、限制并发瓦片请求以及使用 requestAnimationFrame 来优化渲染流程,可以显著提高快速拖动时的性能,避免出现拖动时的卡顿或不流畅问题。

研究发现,通过防抖并不能实际解决瓦片空白问题,又采用了如下方式:

先上可以快速拖动的代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>瓦片类拖动</title><script src="MapUtil.js"></script><style>
/*        地图容器*/.map {position: fixed;width: 100%;height: 100%;left: 0;top: 0;}/*十字架*/.line {position: absolute;background-color: #000;}/*十字架横轴*/.lineX {left: 50%;top: 50%;transform: translate(-50%, -50%);width: 50px;/*改成50px*/height: 2px;}/*十字架纵轴*/.lineY {left: 50%;top: 50%;transform: translate(-50%, -50%);height: 50px;/*改成50px*/width: 2px;}.clearButton{position: absolute;left: 10px;top: 10px;cursor: pointer;}.refreshButton{position: absolute;left: 90px;top: 10px;cursor: pointer;}</style>
</head>
<body><div class="map" id="map">
<!--        画布--><canvas id="canvas"  onmousedown="onMousedown(event)"></canvas>
<!--        十字架横轴--><div class="line lineX"></div>
<!--        十字架纵轴--><div class="line lineY"></div><button class="clearButton" onclick="renderTiles()">重绘画布</button><button class="refreshButton" onclick="onClear()">清除画布</button></div>
</body><script>// 角度转弧度function angleToRad(angle){return angle * (Math.PI / 180)}// 弧度转角度function radToAngle(rad){return rad * (180 / Math.PI)}// 地球半径  单位:米let EARTH_RAD = 6378137// 地球周长let EARTH_PERIMETER = 2 * Math.PI * EARTH_RAD// 瓦片像素let TILE_SIZE = 256// 根据3857坐标及缩放层级计算瓦片行列号function getTileRowAndCol1 (x, y, z){let resolution = getResolution(z)let row = Math.floor(x / resolution / TILE_SIZE)let col = Math.floor(y / resolution / TILE_SIZE)return [row, col]}function getTileRowAndCol2 (x, y, z){x += EARTH_PERIMETER / 2     // ++y = EARTH_PERIMETER / 2 - y  // ++let resolution = getResolution(z)let row = Math.floor(x / resolution / TILE_SIZE)let col = Math.floor(y / resolution / TILE_SIZE)return [row, col]}// 4326转3857function lngLatToMercator(lng, lat){// 经度先转弧度,然后因为 弧度 = 弧长 / 半径 ,得到弧长为 弧长 = 弧度 * 半径let x = angleToRad(lng) * EARTH_RAD// 纬度先转弧度let rad = angleToRad(lat)// 下面我就看不懂了let sin = Math.sin(rad)let y = (EARTH_RAD / 2) * Math.log((1 + sin) / (1 - sin))return [x, y]}// 获取某一层级下的分辨率     n 从0层开始function getResolution(n){let tileNums = Math.pow(2, n)               //一行的瓦片数量console.log("n,tileNums",n,tileNums)let tileTotalPx = tileNums * TILE_SIZE         //一行的瓦片像素点return EARTH_PERIMETER / tileTotalPx           // 每像素点代表多少米}// 分辨率列表let resolutions = []for (let i = 0; i <= 18; i++) {resolutions.push(getResolution(i))}// 计算4326经纬度对应的像素坐标function getPxFromLngLat (lng, lat, z) {let [_x, _y] = lngLatToMercator(lng, lat)// 4326转3857// 转成世界平面图的坐标_x += EARTH_PERIMETER / 2_y = EARTH_PERIMETER / 2 - _ylet resolution = resolutions[z]// 该层级的分辨率// 米/分辨率得到像素let x = Math.floor(_x / resolution)let y = Math.floor(_y / resolution)return [x, y]}// 拼接瓦片地址function getTileUrl(x, y, z){let domainIndexList = [1, 2, 3, 4]let domainIndex =domainIndexList[Math.floor(Math.random() * domainIndexList.length)]return `https://webrd0${domainIndex}.is.autonavi.com/appmaptile?x=${x}&y=${y}&z=${z}&lang=zh_cn&size=1&scale=1&style=8`}// 3857转4326function mercatorToLngLat(x, y){let lng = radToAngle(x) / EARTH_RADlet lat = radToAngle(2 * Math.atan(Math.exp(y / EARTH_RAD)) - Math.PI / 2)return [lng, lat]}</script><script>// 单张瓦片类class Tile {constructor({ctx, row, col, zoom, x, y, shouldRender}) {this.ctx = ctx    // 画布上下文this.row = row    // 瓦片行列号this.col = colthis.zoom = zoom     // 瓦片层级this.x = x      // 显示位置this.y = ythis.shouldRender = shouldRender     // 一个函数,判断某块瓦片是否应该渲染this.url = getTileUrl(this.row, this.col, this.zoom)    // 瓦片urlthis.cacheKey = this.row + '_' + this.col + '_' + this.zoom      // 缓存keythis.img = null     // 图片this.load()}// 加载图片load() {this.img = new Image()this.img.src = this.urlthis.img.onload = () => {this.render()}}// 将图片渲染到canvas上render() {if (!this.shouldRender(this.cacheKey)) {return}this.ctx.drawImage(this.img, this.x, this.y)}// 更新位置updatePos(x, y) {this.x = xthis.y = yreturn this}}// 初始化画布let map=document.getElementById('map')let canvas=document.getElementById('canvas')let { width, height } = map.getBoundingClientRect()// 设置画布大小canvas.width = widthcanvas.height = height// 获取绘图上下文let ctx = canvas.getContext('2d')// 移动画布原点到画布中间,本来画布原点是左上角的ctx.translate(width / 2, height / 2)let tileCache={}//缓存瓦片let currentTileCache={} //记录当前地图上的瓦片let center=[120.148732,30.231006]    //已知变量,中心坐标   雷峰塔坐标let zoom=17    //已知变量, 缩放层级// 容器大小let isMousedown= false//是否按下鼠标renderTiles()//     绘制function renderTiles(){console.log("center=============",center)// 中心点对应的瓦片let centerTile = getTileRowAndCol2(...lngLatToMercator(...center),// 4326转3857zoom                            // 缩放层级)// 中心瓦片左上角对应的像素坐标let centerTilePos = [centerTile[0] * TILE_SIZE,centerTile[1] * TILE_SIZE,];// 中心点对应的像素坐标let centerPos = getPxFromLngLat(...center, zoom)// 中心像素坐标距中心瓦片左上角的差值let offset = [centerPos[0] - centerTilePos[0],centerPos[1] - centerTilePos[1]]// 计算瓦片数量    Math.ceil()向上取整let rowMinNum = Math.ceil((width / 2 - offset[0]) / TILE_SIZE)// 左let colMinNum = Math.ceil((height / 2 - offset[1]) / TILE_SIZE)// 上let rowMaxNum = Math.ceil((width / 2 - (TILE_SIZE - offset[0])) / TILE_SIZE)// 右let colMaxNum = Math.ceil((height / 2 - (TILE_SIZE - offset[1])) / TILE_SIZE)// 下// 渲染画布内所有瓦片currentTileCache = {}; // 清空缓存对象// 从上到下,从左到右,加载瓦片for (let i = -rowMinNum; i <= rowMaxNum; i++) {for (let j = -colMinNum; j <= colMaxNum; j++) {// 当前瓦片的行列号let row = centerTile[0] + ilet col = centerTile[1] + j// 当前瓦片的显示位置let x = i * TILE_SIZE - offset[0]let y = j * TILE_SIZE - offset[1]// 缓存keylet cacheKey = row + '_' + col + '_' + zoom// 记录画布当前需要的瓦片currentTileCache[cacheKey] = trueif (tileCache[cacheKey]) {// 更新到当前位置tileCache[cacheKey].updatePos(x, y).render();}else {// 加载瓦片图片tileCache[cacheKey] = new Tile({ctx: ctx,row,col,zoom: zoom,x,y,// 判断瓦片是否在当前画布缓存对象上,是的话则代表需要渲染shouldRender: (key) => {return currentTileCache[key];},});}}}}// 鼠标按下function onMousedown(e){if (e.which === 1) {isMousedown = true;}}window.addEventListener("mousemove", onMousemove);window.addEventListener("mouseup", onMouseup);// 鼠标移动function onMousemove(e){if (!isMousedown) {return;}// 计算本次拖动的距离对应的经纬度数据let mx = e.movementX * resolutions[zoom];//表示这一次鼠标事件距离上一次鼠标事件横向上的偏移量   ,基本就是 1 像素let my = e.movementY * resolutions[zoom];
// 把当前中心点经纬度转成3857坐标let [x, y] = lngLatToMercator(...center);
// 更新拖动后的中心点经纬度center = mercatorToLngLat(x - mx, my + y);onClear()renderTiles()}// 鼠标松开function onMouseup(){isMousedown = false;}// 清除画布function onClear(){ctx.clearRect(-width / 2,   //参数1 和2 是起始位置-height / 2,width,height);}</script></html>

关键字:深圳有限公司官网_西安凡高网络_网站怎么营销推广_竞价托管是啥意思

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: