From cc4bb60a0295901a386f4bcdef17ae2f9c2e02e3 Mon Sep 17 00:00:00 2001 From: alimu Date: Tue, 4 Jul 2023 11:28:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20'viewport.js'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复下载中断机制,以获取慢网速下的更优秀的加载效果 --- viewport.js | 122 +++++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/viewport.js b/viewport.js index 8518e57..034bae8 100644 --- a/viewport.js +++ b/viewport.js @@ -1,15 +1,16 @@ //help array; let Caches = new Array(); //cache -let downloadRecord = new Array(); -let dm = new Array();//尝试增加下载管理 downloadmanager +//let downloadRecord = new Array(); +let dm = new Array(); //尝试增加下载管理 downloadmanager let alltilecount = 0; let downtilecount = 0; + function ViewPort(canvasId, options) { // allow without 'new' if (!(this instanceof ViewPort)) return new ViewPort(canvasId, options); - this.TILESIZE = 256;//实际的底图大小 + this.TILESIZE = 256; //实际的底图大小 //init this.canvas = document.getElementById(canvasId); this.canvas.width = this.canvas.clientWidth; @@ -18,12 +19,12 @@ function ViewPort(canvasId, options) { this.ctx = this.canvas.getContext('2d'); this.width = this.canvas.clientWidth; this.height = this.canvas.clientHeight; - if (!options) options = {};//options could be null or undefine + if (!options) options = {}; //options could be null or undefine this.minZoomLevel = options.minZoomLevel ?? 0; this.maxZoomLevel = options.maxZoomLevel ?? 21; this.zoomLevel = options.zoomLevel ?? 0; this.dzl = options.dzl ?? 0.25; // zoomlevel offset value - this.rtSize = calTileSize(this.TILESIZE, this.zoomLevel);//named rtSize because real time tile size + this.rtSize = calTileSize(this.TILESIZE, this.zoomLevel); //named rtSize because real time tile size const defaultCenterX = Math.pow(2, this.zoomLevel) * this.TILESIZE / 2; const defaultCenterY = Math.pow(2, this.zoomLevel) * this.TILESIZE / 2; @@ -32,8 +33,8 @@ function ViewPort(canvasId, options) { //如果在options中有指定地图中心点 //options.centerLng && options.centerLat - if (options.centerLat != null && options.centerLng != null - && !isNaN(options.centerLat) && !isNaN(options.centerLng)) { + if (options.centerLat != null && options.centerLng != null && + !isNaN(options.centerLat) && !isNaN(options.centerLng)) { console.log('指定中心点'); try { ({ lat: lat, lng: lng } = this.latlngPreCheck(options.centerLat, options.centerLng)); @@ -47,7 +48,7 @@ function ViewPort(canvasId, options) { } } - this.needtiles = new Array();//需要加载的底图 实时变化 + this.needtiles = new Array(); //需要加载的底图 实时变化 //move let moveflag = false; @@ -64,7 +65,7 @@ function ViewPort(canvasId, options) { moveflag = false; } this.canvas.onmouseout = () => { - moveflag = false;//从元素范围外移动回来时不会继续移动,必须松开左键,按下左键后再进行移动 + moveflag = false; //从元素范围外移动回来时不会继续移动,必须松开左键,按下左键后再进行移动 } this.canvas.onmousemove = (e) => { if (moveflag) { @@ -120,7 +121,7 @@ ViewPort.prototype = { latlngPreCheck(lat, lng) { //数据超界处理 经度 -180 180 //纬度 < 85.0511287798066 (2*Math.atan(Math.pow(Math.E,Math.PI))-Math.PI/2)/Math.PI*180 - const latlimit = 85.0511287798066;//取近似值 + const latlimit = 85.0511287798066; //取近似值 const lngp = lng - Math.floor((lng + 180) / 360) * 360; if (lat < (0 - latlimit) || lat > latlimit) { throw "OutMapRange"; @@ -163,7 +164,7 @@ ViewPort.prototype = { return { lat: lat, lng: lng } }, latlng2Viewport(lat, lng) { - ({ lat: lat, lng: lng } = this.latlngPreCheck(lat, lng));//尝试使用解构赋值 + ({ lat: lat, lng: lng } = this.latlngPreCheck(lat, lng)); //尝试使用解构赋值 const my = lat2y(lat, this.zoomLevel) * this.TILESIZE; const mx = lon2x(lng, this.zoomLevel) * this.TILESIZE; const vp = this.mplat2Viewport(mx, my); @@ -232,17 +233,32 @@ ViewPort.prototype = { tails.push({ x: x, y: y }); } } - this.needtiles = this.tilesFilter(tails);//获取当前视口要渲染的底图 - alltilecount += this.needtiles.length;//for debug + this.needtiles = this.tilesFilter(tails); //获取当前视口要渲染的底图 + let z = this.zoomLevel; //这里的几行代码的逻辑是用到了5个函数里面,后面可以想一下怎么重构 + if (this.zoomLevel != Math.floor(this.zoomLevel)) { + z = Math.floor(this.zoomLevel + 1); + } + + //尝试在这里移除不需要继续下载的内容 + let indexs = new Array(); + dm.forEach(d => { + if (d.z !=z) { + if(this.needtiles.find(e=>e.x==d.x&&e.y==d.y)==undefined){ + d.xhr.abort(); + console.log('中断请求'); + dm.remove(d); + } + } + }); + + + + alltilecount += this.needtiles.length; //for debug //console.log(this.needtiles); this.needtiles.forEach(et => { const etsize = calTileSize(this.TILESIZE, this.zoomLevel); const tilev = this.mplat2Viewport(et.x * etsize, et.y * etsize); this.drawRect(tilev.x, tilev.y, etsize, etsize); - let z = this.zoomLevel;//这里的几行代码的逻辑是用到了5个函数里面,后面可以想一下怎么重构 - if (this.zoomLevel != Math.floor(this.zoomLevel)) { - z = Math.floor(this.zoomLevel + 1); - } const text = `${z}/${et.x}/${et.y}.png`; // console.log(text); this.drawtext(text, tilev.x + etsize / 2, tilev.y + etsize / 2); @@ -287,22 +303,27 @@ function calTileNum(coord, tilesize, zoomLevel) { function lon2tile(lon, z) { return (Math.floor((lon + 180) / 360 * Math.pow(2, z))); } + function lat2tile(lat, z) { return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z))); } + function lon2x(lon, z) { return ((lon + 180) / 360 * Math.pow(2, z)); } + function lat2y(lat, z) { return ((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z)); } + function tile2long(x, z) { return (x / Math.pow(2, z) * 360 - 180); } + function tile2lat(y, z) { var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); @@ -340,25 +361,25 @@ function downloadimg(z, x, y) { //console.log('downloaded' + z + '/' + x + '/' + y + '.png'); downloadRecord.remove(obj); //下载完成后从记录中移除 dm.remove(dmobj); - Caches.push({ img: img, z: z, x: x, y: y });//下载完成后加入缓存 + Caches.push({ img: img, z: z, x: x, y: y }); //下载完成后加入缓存 downtilecount += 1; resolve(img); } img.src = url; - dm.push(dmobj);//尝试添加管理 + dm.push(dmobj); //尝试添加管理 }); } //尝试优化图片下载机制 //采用下载速度比较慢的openstreetmap 测试 function xhrdownloadimg(z, x, y) { return new Promise((resolve, reject) => { - const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`; + const url = `https://b.tile.openstreetmap.org/${z}/${x}/${y}.png`; let imgxhr = new XMLHttpRequest(); imgxhr.open('GET', url, true); //add downloadcheck - const obj = { z: z, x: x, y: y }; - downloadRecord.push(obj); //下载前加入一条记录 + //const obj = { z: z, x: x, y: y }; + //downloadRecord.push(obj); //下载前加入一条记录 const dmobj = { xhr: imgxhr, z: z, x: x, y: y }; //imgxhr.withCredentials = true; @@ -369,12 +390,14 @@ function xhrdownloadimg(z, x, y) { let img = createImageBitmap(blob); Caches.push({ img: img, z: z, x: x, y: y }); downtilecount += 1; - downloadRecord.remove(obj); //下载完成后将记录移除 + //downloadRecord.remove(obj); //下载完成后将记录移除 dm.remove(dmobj); + console.log('dm下载完成减少'); resolve(img); } else { reject('下载似乎失败了'); dm.remove(dmobj); + console.log('dm下载失败减少'); } }; dm.push(dmobj); //保存xhr对象 和xyz索引 @@ -383,51 +406,32 @@ function xhrdownloadimg(z, x, y) { } async function getImg(vp, z, x, y) { - const found = Caches.find(e => (e.z == z && e.x == x && e.y == y));//查找缓存 - if (found == undefined) {//缓存中没有 - const obj = downloadRecord.find(e => e.z == z && e.x == x && e.y == y);//查找下载记录 - if (obj == undefined) { - //如果缓存中没有,并且没有下载记录,需先判断是否继续加载 - if (isTileShouldDownload(vp, z, x, y)) {//如果要继续加载 - //let p = await downloadimg(z, x, y); //发起下载请求并等待结果 - let p = await xhrdownloadimg(z, x, y); - return p; - } - - } else { - //如果缓存中没有,但是有下载记录,需先判断是否继续加载 - if (isTileShouldDownload(vp, z, x, y)) {//如果要继续加载 - return Promise.reject('需要等待下载完成或者重新发起请求'); - } else { - //不需要继续加载了,但是下载请求已经发起 - //不知道提前卸载掉img会不会让下载终止,不然就只能考虑xmlhttprequest了 其有abort方法可以退出下载 - //测试是不行,需转为xmlhttprequest实现 - // dm.remove(dm.find(e => (e.z == z && e.x == x && e.y == y))); - // console.log("may something work"); - - //xhr 中断请求 - const dmobj = dm.find(e => (e.z == z && e.x == x && e.y == y)); - if (dmobj) { - dmobj.xhr.abort(); - dm.remove(dmobj); - console.log("主动中断 ", { z: z, x: x, y: y }); - } - } + const found = Caches.find(e => (e.z == z && e.x == x && e.y == y)); //查找缓存 + if (found == undefined) { //缓存中没有 + console.log('缓存中没有' + z + '/' + x + '/' + y + '.png'); + //这里不需要判断是否继续加载,在还未开始下载图片前,这里可以说是立即执行的 + console.log(isTileShouldDownload(vp, z, x, y));//always true + const dmobj = dm.find(e => (e.z == z && e.x == x && e.y == y)); + if (dmobj == undefined) { + //如果缓存中没有,并且没有下载记录 发起下载请求并等待结果 + let p = await xhrdownloadimg(z, x, y); + return p; + } + else {//有下载记录就不用继续下载了 + console.log(vp.zoomLevel, vp.xoffset, vp.yoffset); + return Promise.reject('需要等待下载完成'); } - } else {//缓存中有直接返回 - //console.log('exist' + z + '/' + x + '/' + y + '.png'); + } else { //缓存中有直接返回 + console.log('缓存中有' + z + '/' + x + '/' + y + '.png'); return found.img; } } - - function debug() { console.log('=============debug================='); console.log('alltilecount', alltilecount); console.log('downtilecount', downtilecount); console.log('cachecount', Caches.length); - console.log('drecord count', downloadRecord.length); console.log('dm count', dm.length); console.log('=============debug================='); -} +} \ No newline at end of file