0%

Introduce

近期项目接触到gis到东西,我选择了openlayers6这个开源库来做项目开发,踩来几个坑,记录一下。

加载自定义图片作为底图

在加载自定义图片作为底图时,我把url给image对象

1
2
3
const img = new Image()
const url = '/static/img.ppng'
img.url = url

在渲染map对象时,我用到是layer下的image,在给定的source这个属性值,我用ImageCanvas来渲染自定义的图层。这是一开始写的代码,但是在这段代码中,会有两个问题:

  • 第一个问题是在画布初始渲染的时候会渲染不出来的,必须在缩放层级之后画布才能正常显示;
  • 第二个问题就是不管怎么滚动或者平移,画布总会自动聚焦到中心。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
return new ImageCanvas({
canvasFunction: (
extent,
resolution,
pixelRatio,
size
) => {
const canvas = document.createElement('canvas');
canvas.width = size[0];
canvas.height = size[1];
const img = new Image()
const url = '/static/img.ppng'
img.url = url
img.onolad = function(){
ctx.drawImage(img, 0, 0, size[0], size[1]);
}
return canvas;
},
projection: 'EPSG:3857'
});

首先第一个问题解决方案,那就是在img到onload里面来进行一个底图渲染的工作。
第二个问题的原因是因为openlayers 中不管对地图平移还是缩放,都会进行重绘的操作,所以解决方案就是设置一个临时变量来判断是否是第一次绘制。

like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
return new ImageCanvas({
canvasFunction: (
extent,
resolution,
pixelRatio,
size /*, projection*/
) => {
const canvas = document.createElement('canvas');
canvas.width = size[0];
canvas.height = size[1];

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const ctx = canvas.getContext('2d')!;
// 这里必须要做一个判断,每次的范围变动都会引起重绘,从而触发该回调函数
if (this.isFirst) {
this.isFirst = false;
ctx.beginPath();
ctx.drawImage(img, 0, 0, size[0], size[1]);
ctx.closePath();

return canvas;
}
},
projection: 'EPSG:3857'
});

底图限制拖动范围

在view中定义extent即可

在移动feature时限制在容器之内

  • 首先要限制范围,首选需要获取到容器到边界。获取view的extent即可
  • 其次需要在移动过程中获取多边形的边界。调用getExtent即可
  • 然后需要做临界值判断,根据boolean值来判断是否允许移动,容器边界跟feature边界比较即可。
  • 最后怎么来移动,参考库中自带的Translate可以发现,是获取到feature到geometry到translate方法来移动,当判断临界值时,移动的距离设置为0即可。

为了应对不同分辨率或者容器大小下的限制范围 — 由于extent不会自动针对分辨率或者容器大小来计算,所以需要使用calculateExtent来根据容器大小获取最新的extent

以上 需要重写部分Translate的实现

在编辑feature时同样需要限制范围。

原理同移动feature,但是略复杂

undo redo的实现

因为需求中仅要记录移动feature和编辑feature,因此在undo redo的时候就是在addFeature和removeFeature中来回切换,只要记录当前操作的undo操作和redo操作即 — 在项目中undo 和redo的过程其实就是清除绘画和重新绘画的过程,我大概试了上百次,performance表现良好,一片绿色,但是记得在新增新的modify或者translate的时候要把上次的清除,不然有性能问题,非常卡。

点击feature实行交互,点击空白取消交互,点击其他feature,当前交互,上次取消交互。

之前是自己直接用click事件来做处理,后来发现selet这个可以帮我处理点击和取消点击的样式问题,但是仍然需要我自己处理交互问题,每次点击feature的时候其实就是把上次的modify和translate删除,然后重新添加一份当前点击feature的modify和translate,

removeNonASCII

Removes non-printable ASCII characters.

Use a regular expression to remove non-printable ASCII characters.

1
const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, '')
1
removeNonASCII('äÄçÇéÉêlorem-ipsumöÖÐþúÚ'); // 'lorem-ipsum'
阅读全文 »

创建一个节流函数在给定的毫秒中最多调用一次

Creates a throttled function that only invokes the provided function at most once per every wait milliseconds


使用setTimeout 和 clearTimeout 对给定对方法节流。fn使用Function.prototype.apply()this上下文应用到函数中并且提供必要到参数arguments。使用Date.now()收集上一次函数的调用时间。没有给定第二个参数 wait的情况,默认设置时间间隔为0

阅读全文 »

NaN 和 Number.isNaN


全局属性 NaN 的值表示不是一个数字(Not-A-Number)。

NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。在现代浏览器中(ES5中), NaN 属性是一个不可配置(non-configurable),不可写(non-writable)的属性。但在ES3中,这个属性的值是可以被更改的,但是也应该避免覆盖。 — mdn

阅读全文 »

null vs undefined

The ECMAScript language specification describes them as follows:

  • undefined 是被使用但是没有被赋值
  • null 是对变量一个显示对声明一个值

在JS中,每个变量能够同时被赋值引用类型和原始类型。因此,如果null意味着 不是一个对象,JS也需要一个初值意味着 既不是 引用类型 也不是 原始类型。那个值就是undefined。

阅读全文 »

  1. focus

    • Options

      An optional object providing options to control aspects of the focusing process. This object may contain the following property:

      • preventScroll Optional

        A Boolean value indicating whether or not the browser should scroll the document to bring the newly-focused element into view. A value of false for preventScroll (the default) means that the browser will scroll the element into view after focusing it. If preventScroll is set to true, no scrolling will occur.

  2. 计算元素的scrollTop,让滚动列表的scrollTop的值等于元素的scrollTop

  3. scrollIntoView

    文档

  1. 在vue ts class给数组添加值时报错
    1
    2
    3
    4
    5
    6
    7
    export default class App{
    list =[]
    _push(){
    this.list.push(1)
    # error
    }
    }
    由于定义的list是never类型的数组,所以不能够直接添加元素,所以在定义数组的时候需要显示定义类型
    1
    list = [] as number[]
  2. line-height 属性同样会影响伪元素的高度
  3. ts class 自定义vue双向绑定
    v-model默认监听input事件,如果是checkbox或者radio
    1
    2
    @Modle('change') value:any
    # 这样vue会监听change,$emit('change')改变外部绑定的值

在执行下面这句代码的时候

1
hexo clean && hexo g -d

报错信息

1
2
3
4
5
Something's wrong. Maybe you can find the solution here: https://hexo.io/docs/troubleshooting.html
TypeError [ERR_INVALID_ARG_TYPE]: The "mode" argument must be integer. Received an instance of Object
at copyFile (fs.js:1924:10)
at tryCatcher (/Users/zjs/Desktop/blog/node_modules/bluebird/js/release/util.js:16:23)
at ret (eval at makeNodePromisifiedEval (/Users/zjs/.nvm/versions/node/v14.10.0/lib/node_modules/hexo-cli/node_modules/bluebird/js/release/promisify.js:184:12), <anonymous>:13:39)

解决方案
NodeJs 版本从 14 回到 12 就🉑️!

上个html代码

1
2
3
4
5
<div class="parent">
<div class="child">Child</div>
<div class="child">Child</div>
<div class="child">Child</div>
</div>

css≠

1
2
3
4
5
6
.parent {
display: flex;
}
.child {
flex: 0 1 auto; /* Default flex value */
}
Child
Child
Child
1
2
3
.child {
flex: [flex-grow] [flex-shrink] [flex-basis];
}

flex-basic 默认是auto,告诉元素保持一个理想的尺寸;但是默认情况下的元素宽度是多少?auto告诉元素它的宽度由它的内容决定。为了让子元素占据父元素的所有空间,可以给子元素的宽度设置为width:100%,或者flex-basis:100%,或者flex-grow:1。假如有个宽为700px的容器,里面两个子元素分别设置了flex-basic:200pxflex-basic:300px,那么占据空间的计算会以700 - 200 - 300 之后再进行分配空间。

当给flex 的第三值 ,也就是 flex-basis 设置成1000px时,它会试着占据1000px的空间,如果不行,它会等比例占据其他元素的空间,但是其他元素内的文本长度同样会影响这个元素的位置分配;但是在更小的屏幕上可能会发现实际宽度并没有1000px,这是因为给了shrink,这个值告诉元素需要它等值缩小

1
2
3
.child-three {
flex: 0 1 1000px;
}

如果grow 和 shrink都是0,那么元素不会等比例压缩,basis如果超出父元素宽度,那直接超出

1
2
3
.child-three {
flex: 0 0 1000px;
}