0%

js 异步

https://www.w3schools.com/js/js_async.asp

search()的参数是正则表达式,而indexOf()的参数只是普通的字符串。indexOf()是比search()更加底层的方法。

如果只是兑一个具体字符串来茶渣检索,那么使用indexOf()的系统资源消耗更小,效率更高;如果查找具有某些特征的字符串(例如查找以a开头,后面是数字的字符串),那么indexOf()就无能为力,必须要使用正则表达式和search()方法了。

大多是时候用indexOf()不是为了真的想知道子字符串的位置,而是想知道长字符串中有没有包含这个子字符串。若果返回索引为-1,那么说明没有,反之则有。

startsWith

startsWith endsWith includes

search find findIndex

includes

似乎比 indexOf 厉害一点

1
2
3
var arr = ['a','b', 'c', NaN];
console.log(arr.indexOf(NaN)) // -1
console.log(arr.includes(NaN)) // true
1
2
3
var arr = new Array(3);
console.log(arr.indexOf(undefined));//-1
console.log(arr.includes(undefined))//true

fill 的小问题

1
2
3
a = Array(10).fill({'hello':'goodbye'});
a[0].hello= 777
// 全部都会变
1
2
aa = Array(10).fill().map(k=>({hello:'world'}));
aa[0].hello= 777

// 只有第一个会变 // 以下皆是

1
aaa = Array.from({length:10},()=> ({'hello':'goodbye'}))
1
aaaa = [...new Array(10)].map(()=> ({'hello':'goodbye'}));

repeat

https://www.samanthaming.com/tidbits/22-2-ways-to-repeat-strings/

break

1
2
3
4
for(let i =0;i< 10;i++){
if(i>5)break
console.log(i)
}

find 或者 findIndex

  • ref 使用 aaa.value
  • reactive 类型是 object

ref 和 reactive 一个针对原始数据类型,而另一个用于对象,这两个API都是为了给JavaScript普通的数据类型赋予响应式特性(reactivity)。

  • reactive和ref都是用来定义响应式数据的,而reactive更推荐用来定义对象,ref更推荐定义基础数据类型,但是ref也可以定义数组和对象
  • 在访问数据的时候,ref需要使用.value,而reactive不需要
1
const dataAsRefs = toRefs(data);

步子比蛋大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>Hello, {{ name }}!</div>
<input v-model="name" />
<button :disabled="!isNamePresent" @click="submitName">Submit</button>
</template>

<script setup>
import { ref, computed } from 'vue'

const name = ref('')
const isNamePresent = computed(() => name.value.length > 0)

function submitName() {
console.log(name.value)
}
</script>

父子组件

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Form.vue -->
<template>
<form @submit.prevent="submitHandler">
<label>Name
<input type="text" />
</label>
<button>Submit</button>
</form>
</template>

<script setup>
function submitHandler() {
// Do something
}
</script>

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- App.vue -->
<template>
<div>Hello, {{ name }}!</div>
<Form />
</template>

<script setup>
import { ref } from 'vue'
import Form from './components/Form.vue'

const name = ref('')

function submitForm() {
console.log(name.value)
}
</script>

defineProps 和 defineEmits

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!-- Form.vue -->
<template>
<form @submit.prevent="submitHandler">
<label>Name
<input v-model="name" type="text" />
</label>
<button>Submit</button>
</form>
</template>

<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue', 'submit']);

const name = computed({
get () {
return props.modelValue
},
set(val) {
emit('update:modelValue', val);
}
})

function submitHandler() {
emit('submit')
}
</script>

<!-- App.vue -->
<template>
<div>Hello, {{ name }}!</div>
<Form v-model="name" @submit="submitForm" />
</template>

<script setup>
import { ref } from 'vue'
import Form from './components/Form.vue'

const name = ref('')

function submitForm() {
console.log(name.value)
}
</script>

git pull是git fetch和git merge两个步骤的结合

vscode 的 sync 等于下面

1
2
git pull origin someBranch
git push origin someBranch

  • ~ 选择器的 相反的 “previous sibling” selector ?

几个思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* default link color is blue */
.parent a {
color: blue;
}

/* prev siblings should be red */
.parent:hover a {
color: red;
}
.parent a:hover,
.parent a:hover ~ a {
color: blue;
}
1
2
3
4
5
6
7
<div class="parent">
<a href="#">link</a>
<a href="#">link</a>
<a href="#">link</a>
<a href="#">link</a>
<a href="#">link</a>
</div>

flex order 或者 row-reverse 。 DOM 在后但显示“在前”。

1
ul li:nth-child(-n+2)

via https://stackoverflow.com/questions/1817792/is-there-a-previous-sibling-selector

via https://coolcssanimation.com/how-to-trigger-a-css-animation-on-scroll/


IntersectionObserver 是否可见

1
2
3
4
5
6
7
8
9
10
11
12
13
var io = new IntersectionObserver(callback, option);

// 开始观察
io.observe(document.getElementById('example'));

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

io.observe(elementA);
io.observe(elementB);

lazy load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function query(selector) {
return Array.from(document.querySelectorAll(selector));
}

var observer = new IntersectionObserver(
function(changes) {
changes.forEach(function(change) {
var container = change.target;
var content = container.querySelector('template').content;
container.appendChild(content);
observer.unobserve(container);
});
}
);

query('.lazy-loaded').forEach(function (item) {
observer.observe(item);
});

无限滚动(infinite scroll)

1
2
3
4
5
6
7
8
9
10
11
12
var intersectionObserver = new IntersectionObserver(
function (entries) {
// 如果不可见,就返回
if (entries[0].intersectionRatio <= 0) return;
loadItems(10);
console.log('Loaded new items');
});

// 开始观察
intersectionObserver.observe(
document.querySelector('.scrollerFooter')
);

ResizeObserver

观测 resize

scrollIntoView 到中间

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

scrollIntoView()

默认是

scrollIntoView(true)

也就是

scrollIntoView({block: “start”, inline: “nearest”})

false 是

scrollIntoView({block: “end”, inline: “nearest”})

参数

behavior Optional

Defines the transition animation. One of auto or smooth. Defaults to auto.

block Optional

Defines vertical alignment. One of start, center, end, or nearest. Defaults to start.

inline Optional

Defines horizontal alignment. One of start, center, end, or nearest. Defaults to nearest.

tab 尽量靠中间

1
2
3
4
5
6
7
obj.scrollIntoView(
{
behavior: 'smooth',
block: 'nearest',
inline: 'center'
}
)

16:9 固定比例的 iframe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.container {
position: relative;
overflow: hidden;
width: 100%;
padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
}

/* Then style the iframe to fit in the container div with full height and width */
.responsive-iframe {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
}

class add active

1
2
3
4
5
6
7
8
9
10
11
12
13
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");

// If there's no active class
if (current.length > 0) {
current[0].className = current[0].className.replace(" active", "");
}

// Add the active class to the current/clicked button
this.className += " active";
});
}

Get Element in Iframe

1
2
3
var iframe = document.getElementById("myFrame");
var elmnt = iframe.contentWindow.document.getElementsByTagName("H1")[0];
elmnt.style.display = "none";

Media Queries with JavaScrip

1
2
3
4
5
6
7
8
9
10
11
function myFunction(x) {
if (x.matches) { // If media query matches
document.body.style.backgroundColor = "yellow";
} else {
document.body.style.backgroundColor = "pink";
}
}

var x = window.matchMedia("(max-width: 700px)")
myFunction(x) // Call listener function at run time
x.addListener(myFunction) // Attach listener function on state changes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<defs>
<linearGradient
id="grad1"
x1="0%"
y1="0%"
x2="100%"
y2="0%"
>
<stop
offset="0%"
style="stop-color:#8095F0;stop-opacity:1"
/>
<stop
offset="100%"
style="stop-color:#51FCB5;stop-opacity:1"
/>
</linearGradient>
</defs>

1
2
3
4
5
6
'fill-color': ['interpolate-hcl', ['linear'], ['feature-state', 'population'], 0, 'red', 1e6, 'blue']


for (const district of districts) {
map.setFeatureState({ source: 'mysource', sourceLayer: 'mysourcelayer', id: district.id }, { population: district.population});
}
1
2
3
4
5
6
'fill-color': ['match', ['get', 'id'],
1020, 'red',
1033, 'green',
1038, 'red',
1049, 'white',
// ...
1
2
3
4
5
6
7
8
"icon-image": [
"case",
["all", ["==", ["get", "matched"], "True"], ["!=", ["get", "vehicle"], ["get", "standstill_vehicle"]]],
"orangemarker",
["==", ["get", "matched"], "True"],
"greenmarker",
"redmarker",
]
1
2
3
4
5
this.map.setPaintProperty("somelayer", "fill-color",
["case",
["==", ["get", "some_prop"], someval], "#34c0dd",
"#499bbc"
]);
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
26
27
28
29
30
31
32
33
34
35
const layers = [{
id: 'countries',
type: 'fill',
source: 'countries',
paint: {
'fill-color': [
'match', ['get', 'filter_prop', ['global']],
'pop_est',
['step', ['get', 'pop_est'],
0, '#f8d5cc',
1000000, '#f4bfb6',
5000000, '#f1a8a5',
10000000, '#ee8f9a',
50000000, '#ec739b',
100000000, '#dd5ca8',
250000000, '#c44cc0',
500000000, '#9f43d7',
1000000000, '#6e40e6',
],
'gdp_md_est',
['step', ['get', 'gdp_md_est'],
0, '#f8d5cc',
1000, '#f4bfb6',
5000, '#f1a8a5',
10000, '#ee8f9a',
50000, '#ec739b',
100000, '#dd5ca8',
250000, '#c44cc0',
5000000, '#9f43d7',
10000000, '#6e40e6',
],
'#ffffff',
],
},
}];

以下这个未测试!

1
2
3
4
5
6
['step', ['zoom'],
['case', ['all', ['!', ['feature-state', 'hidden']], ['get', 'red'], 1, 0],
10,
['case', ['all', ['!', ['feature-state', 'hidden']], ['any', ['get', 'red'], ['get', 'green']], 1, 0],
15,
['case', [['!', ['feature-state', 'hidden']], 1, 0]],
1
2
3
4
5
6
7
8
9
10
11
12
13
14
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "grundbuch",
filter: ["has", "point_count"],
layout: {
"text-field": "{point_count_abbreviated}",
"text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"],
"text-size": 12
},
paint: {
"text-color": "#ffffff"
}
});

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
"line-color": [
'case',
['boolean', ['feature-state', 'hover'], false],
'#c00',

[
'case',
['!=', ['get', 'color'], null],
['get', 'color'],
'#16e8e9'
]

],
````

###

test.map.showCollisionBoxes = true // 显示碰撞边界
test.map.showTileBoundaries = true // 显示tile边界
test.map.showOverdrawInspector = true

需要很习惯一段时间

### setFeatureState

if (quakeID) {
map.removeFeatureState({
source: “earthquakes”,
id: quakeID
});
}
map.setFeatureState({
source: ‘earthquakes’,
id: quakeID,
}, {
hover: true
});

1
2


map.addLayer({
‘id’: ‘earthquakes-viz’,
‘type’: ‘circle’,
‘source’: ‘earthquakes’,
‘paint’: {
// The feature-state dependent circle-radius expression will render
// the radius size according to its magnitude when
// a feature’s hover state is set to true
‘circle-radius’: [
‘case’,
[‘boolean’,
[‘feature-state’, ‘hover’],
false
],
[
‘interpolate’, [‘linear’],
[‘get’, ‘mag’],
1, 8,
1.5, 10,
2, 12,
2.5, 14,
3, 16,
3.5, 18,
4.5, 20,
6.5, 22,
8.5, 24,
10.5, 26
],
5
],
‘circle-stroke-color’: ‘#000’,
‘circle-stroke-width’: 1,
// The feature-state dependent circle-color expression will render
// the color according to its magnitude when
// a feature’s hover state is set to true
‘circle-color’: [
‘case’,
[‘boolean’,
[‘feature-state’, ‘hover’],
false
],
[
‘interpolate’, [‘linear’],
[‘get’, ‘mag’],
1, ‘#fff7ec’,
1.5, ‘#fee8c8’,
2, ‘#fdd49e’,
2.5, ‘#fdbb84’,
3, ‘#fc8d59’,
3.5, ‘#ef6548’,
4.5, ‘#d7301f’,
6.5, ‘#b30000’,
8.5, ‘#7f0000’,
10.5, ‘#000’
],
‘#000’
]
}
});

````

via https://docs.mapbox.com/help/tutorials/create-interactive-hover-effects-with-mapbox-gl-js/

examples

var aa = { “type”: “Polygon”,
“coordinates”: [
[
[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
]
}

var aa = { “type”: “Polygon”,
“coordinates”: [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],
[ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
]
}

test.map.getSource(‘temp_line’).setData(aa)

{
type:’FeatureCollection’,
features:[
{
“geometry”: {
“type”: “MultiPoint”,
“coordinates”: [
[
108.91373500227928,
34.241324229728534
],
]
},
“type”: “Feature”,

}
]
}

1
Array.prototype.push.apply(a, [1,2]))

a.push(1, 2, 3);

1
2
3
4
var arr = [1];
var newItems = [2, 3];
arr.push(...newItems);
console.log(arr);

思路如下(console中有三个数据)

  • 后台请求(adss类型的)物理光缆 wlgl
  • 获得(界面中)铁路数据 railway
  • 计算 railway 和 wlgl 交叉点
  • groupBy 业务光缆,items push 所有点。然后去重为 items2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function demo() {
console.log('Taking a break...');
await sleep(2000);
console.log('Two seconds later, showing sleep in a loop...');

// Sleep in loop
for (let i = 0; i < 5; i++) {
if (i === 3)
await sleep(2000);
console.log(i);
}
}

demo();

via https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep

layer chnage source

其实是重建

1
2
3
4
5
6
7
8
9
10
11
12
function setLayerSource (layerId, source, sourceLayer) {
const oldLayers = map.getStyle().layers;
const layerIndex = oldLayers.findIndex(l => l.id === layerId);
const layerDef = oldLayers[layerIndex];
const before = oldLayers[layerIndex + 1] && oldLayers[layerIndex + 1].id;
layerDef.source = source;
if (sourceLayer) {
layerDef['source-layer'] = sourceLayer;
}
map.removeLayer(layerId);
map.addLayer(layerDef, before);
}

https://github.com/mapbox/mapbox-gl-js/issues/1722

  • 为对象添加属性
  • 为对象添加方法
  • 浅克隆
  • 合并多个对象
  • 属性指定默认值
1
2
3
4
5
6
7
const DEFAULTS = {
logLevel: 0,
outputFormat: 'html'
};
function processContent(options) {
let options = Object.assign({}, DEFAULTS, options);
}

Object.create()、new Object()和{}的区别

直接字面量创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var objA = {};
objA.name = 'a';
objA.sayName = function() {
console.log(`My name is ${this.name} !`);
}
// var objA = {
// name: 'a',
// sayName: function() {
// console.log(`My name is ${this.name} !`);
// }
// }
objA.sayName();
console.log(objA.__proto__ === Object.prototype); // true
console.log(objA instanceof Object); // true

new关键字创建

1
2
3
4
5
6
7
8
9
var objB = new Object();
// var objB = Object();
objB.name = 'b';
objB.sayName = function() {
console.log(`My name is ${this.name} !`);
}
objB.sayName();
console.log(objB.__proto__ === Object.prototype); // true
console.log(objB instanceof Object); // true

Object.create 创建

一个完整的版本

https://ouyangresume.github.io/2019/12/19/mapbox%E7%9A%84Popup%E4%B8%8EVue%E6%A1%86%E6%9E%B6%E8%9E%8D%E5%90%88%E4%BD%BF%E7%94%A8/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import StarRating from 'vue-star-rating'

// ...

var starRating = new Vue({
...StarRating,
parent: this,
propsData: { /* pass props here*/ }
}).$mount()

starRating.$on('someEvent', (value) => {
// listen to events emitted from the component
})
var popup = new mapboxgl.Popup()
.setDOMContent(startRating.$el)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import InfoWindowContent from '../InfoWindowContent.vue'

L.popup({maxWidth: 600,
className: 'leaflet-info-window'})
.setLatLng(latlng)
.setContent('<div id="info-window"></div>')
.openOn(this.map)
const InfoWindow = Vue.extend(InfoWindowContent)
const infoWindow = new InfoWindow({
propsData: {
windowData: this.infoWindowData,
windowType: this.infoWindowType
},
store: this.$store
})
infoWindow.$mount("#info-window")
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
26
27
28
// 添加Popup对象
const popup = new mapboxgl.Popup({
anchor: "bottom",
offset: offset || [1, -15],
className: "popup"
});
popup.on("close", function(p) {
if (p && p.target && p.target._vue) {
p.target._vue.$destroy();
}
});
const dom = document.createElement("div");
dom.className = "popup-content-wrapper";

popup.setLngLat(coordinates).setDOMContent(dom).addTo(map);
// 渲染局部Vue组件(mappopupwrapper)
popup._vue = new Vue({
render: h =>
h(MapPopupWrapper, {
props: {
width: "700px",
type: "resource",
target: {
feature
}
}
})
}).$mount(dom);