董懂 发布的文章

UPDATE 2023-05-04

某些情况下,css 属性inset 不被支持,造成本文介绍的方法失效。此时只要用 lefttop 加上宽高替换 inset 就好了。下面的代码已经修改。


Element Plus 的 Table 组件文档写道

只要在 el-table 元素中定义了 height 属性,即可实现固定表头的表格,而不需要额外的代码。

可是有几个场景能知道 Table 的固定高度?所以文档中的方法不太可行。

尝试使用 :deep() 深度选择器改变 el-table 的样式,使它形成 overflow-y: auto 的区域,就会触发一个非常奇怪的行为:el-table 的宽度会一直慢慢变大!因为看起来是脚本控制的,也就没有精力深究。

最后还是在 CSDN 上找到的办法,使用绝对定位。我的实践,下面的代码是最方便的:

HTML:

<div class="box">
    <div class="menu">菜单之类的顶部区域,不需要可以去掉</div>
    <div class="table-box">
        <el-table class="el-table"></el-table>
    </div>
</div>

CSS:

.box {
    display: flex;
    flex-direction: column;
}

.menu {
    flex: none;
    height: 100px;
}

.table-box {
    flex: auto;
    overflow: hidden;
    position: relative;
}

.el-table {
    position: absolute;
    - inset: 0;
    + left: 0;
    + top: 0;
    + width: 100%;
    + height: 100%;
}

现在,el-table 就会在固定表头的同时,使用内置的 el-scrollbar 来滚动表格内容了。

End

uni-app的坑还是挺多的,这是今天遇到的一个坑,话不多说,上代码:

<view v-if="condition1"> 1 </view>
<template v-else-if="condition2">
    <view> 2 </view>
</template>

上面的代码中,template中的内容即使条件为 true,也可能在真机上不会显示出来。

解决的办法就是不要混用,如果确实要用到 template,那就全部用 template:

<template v-if="condition1">
    <view> 1 </view>
</template>
<template v-else-if="condition2">
    <view> 2 </view>
</template>

对于reactive类型的响应式数据,只需要按照文档中介绍的方法建立侦听器:

const x = reactive({ a: 1 });

watch(() => x.a, val => alert(1));

对于ref类型的数据,则需要多加一个value来获取到正确的侦听对象:

const x = ref({ a: 1 });

watch(() => x.value.a, val => console.log(val););

使用时必须注意,否则无法正确侦听。

对于数组、对象类型的数据,使用reactive比较方便,因为不需要使用.value获取此类数据的值。

element-plus 的图标其实是一个个的组件:

JavaScript:

import { IconName, IconName2 } from '@element-plus/icons-vue';

Template:

<el-icon>
    <IconName/>
</el-icon>

使用 :is 切换组件:

<component :is="bool ? IconName : IconName2"></component>

然而 :is 的值必须是引入的组件,不能是字符串,否则就会渲染成这个样子:

<iconname></iconname>

如果菜单结构单独放在一个文件中,比如 menu.js

export default [
    {
        name: 'Menu 1',
        icon: 'IconName'
    }
]

这里的 icon的值就不能是字符串,需要是一个组件对象的引用。这就需要先在 menu.js 中引入图标组件:

import { IconName } from '@element-plus/icons-vue';

然后将组件传给 icon

export default [
    {
        name: 'Menu 1',
        icon: IconName
    }
]

这样,就能够在组件中正确显示图标了。当然,这时候组件中就不需要再引入图标了。


End

好多小伙伴都看到过这样一段全是由各种括号,以及有限的几个操作符组成的代码,完整代码贴在下面。为了好看,我给它格式化了一下:

[]
[
    (![] + [])[+[]] + 
    ([![]] + [][[]])[+!+[] + [+[]]] +
    (![] + [])[!+[] + !+[]] +
    (!![] + [])[+[]] +
    (!![] + [])[!+[] + !+[] + !+[]] +
    (!![] + [])[+!+[]]
]
[
    (
        [][
        (![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] + [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] +
        (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]] + []
    )[!+[] + !+[] + !+[]] +
    (
        !![] +
        [][(![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] + [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] +
        (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]]
    )[+!+[] + [+[]]] +
    ([][[]] + [])[+!+[]] +
    (![] + [])[!+[] + !+[] + !+[]] +
    (!![] + [])[+[]] +
    (!![] + [])[+!+[]] +
    ([][[]] + [])[+[]] +
    (
        [][(![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] + [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] +
        (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]] + []
    )[!+[] + !+[] + !+[]] +
    (!![] + [])[+[]] +
    (
        !![] +
        [][(![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] +
        [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] + (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]]
    )[+!+[] + [+[]]] +
    (!![] + [])[+!+[]]
]
(
    (![] + [])[+!+[]] +
    (![] + [])[!+[] + !+[]] +
    (!![] + [])[!+[] + !+[] + !+[]] +
    (!![] + [])[+!+[]] +
    (!![] + [])[+[]] +
    (
        ![] +
        [][
        (![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] + [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] +
        (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]
        ]
    )[!+[] + !+[] + [+[]]] +
    [+!+[]] +
    (
        !![] +
        [][(![] + [])[+[]] +
        ([![]] + [][[]])[+!+[] + [+[]]] +
        (![] + [])[!+[] + !+[]] +
        (!![] + [])[+[]] +
        (!![] + [])[!+[] + !+[] + !+[]] +
        (!![] + [])[+!+[]]]
    )[!+[] + !+[] + [+[]]]
)
();

这是一坨什么玩意儿?看得人脑瓜子嗡嗡的。
今天上班第一天,不想干活,只想摸鱼。趁着这个时间娱乐一下,看看这段代码到底是什么东西。

从上面的代码中可以看出来,我已经把它的主要结构展示地非常清晰:

//① ②    ③    ④   ⑤
 [][...][...](...)();

非常明显,第一个[]肯定是提供了一个存在于它的原型链上的 方法 或者 属性 供第二个[]使用。既然是用方括号调用方法或者属性,那么第二个[]里面必定是一个字符串,就像这样:

[]['map'];
//等价于
Array.prototype.map;

当然,第二个[]内的字符串不一定是map,我只是用map举个栗子,具体内容还要进一步分析。

以此类推,第三个[]类似第二个[],举例来说就是这样:

[]['map']['name'] //'map'

上面那行示例代码获取到了函数Array.prototype.mapname属性,但本文讨论的代码究竟获取到了什么东西,还是留待进一步分析。

咱们继续往下看。第四部分是一个小括号()。小括号除了分块外,最大的作用是调用函数。综合上文的分析,第三部分应该是返回一个方法,而第四部分的这个小括号就是调用第三部分返回的方法,小括号里面的内容呢,那肯定就是参数了。

至于第五部分也就是最后一个小括号,很明显是调用方法。既然这样,那么前四部分整体返回的就是一个方法,也就是说,第四部分执行了一个返回方法的方法。

什么方法会返回一个方法呢?继续深入分析。

第一部分[]是一个空的数组实例,除了继承了Array的一些属性和方法,其他没什么可分析的。

第二部分是由5个+操作符连接的语句。下面以第二部分的第一行为例进行分析。
第一行是(![] + [])[+[]],又可以分解成(![] + [])[+[]],其中涉及到的知识就是隐式转换。其中:

![]
//等价于
!''

结果是false,于是

(![] + [])
//等价于
(false + [])
//等价于
(false + '')
//等价于
('false' + '')
//等于
'false'

隐式转换的具体规则网上已经有很多文章了,这里就不重复了。

[+[]]
//等价于
[+'']
//等价于
[0]

所以,第一行最后的结果是

'false'[0]
//等于
'f'

搞了半天,原来是为了获取'f'这个字符。

实际上,分析完上面这行代码,其他的就不言自明了,套路都是一样的,第二部分和第三部分的目的,是为了拼凑'filter''constructor'这两个字符串。

经过分析,整个代码相当于:

[]['filter']['constructor']('alert(1)')();

等于

[].filter.constructor('alert(1)')()

那么为啥拼凑'filter'这个字符串,而不是拼凑'map''sort'……呢?这里必须要好好说道说道,这是因为,'filter'这个字符串,它容易得到……

[].filter.constructor指向的是构造函数Function,利用它,可以使用字符串构造函数,类似于eval(),所以[].filter.constructor('alert(1)')相当于

Function('alert(1)')

执行Function('alert(1)'),就会弹出 1 了:

Function('alert(1)')()

好了,本文到此结束了。


End

0、开始

本篇文章介绍如何使用高德地图 Web API,在地图中显示具有高度和面的自定义多边形标注。

1、准备工作

首先申请高德地图的key。申请完毕之后,新建一个空白的 html 页面,引入需要的资源:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>高德地图开发示例</title>
    <style>
        html,
        body,
        #map {
            height: 100%;
        }
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <div id="map"></div>
</body>

</html>
<script src="https://webapi.amap.com/maps?v=2.0&key=your_key"></script>
<script src="https://webapi.amap.com/loca?v=2.0.0&key=your_key"></script>
<script>
    //我们就在这里编写代码
</script>

2、初始化

初始化地图:

var map = new AMap.Map('map', {
    zoom: 17.6,
    viewMode: '3D', //使用3D模式才能显示3D标注
    pitch: 45, //倾斜角度
    mapStyle: 'amap://styles/darkblue', //使用黑色地图看起来更神秘…
    center: [116.40,39.92], //使用故宫作为地图中心点
    showBuildingBlock: false, //不显示建筑块
    showLabel: false, //不显示街道名之类的标签
});

接着初始化Loca

var loca = window.loca = new Loca.Container({ map });

3、获取多边形数据

接受的数据是标准GeoJSON格式的,我们需要准备GeoJSON格式的数据。格式如下:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {},
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            lng,
                            lat
                        ],
                        ......
                    ]
                ]
            }
        },
        ......
    ]
}

有4种方法获取数据

  1. 使用阿里的DataV.GeoAtlas地理小工具,不过,对于一些偏远地区,地图上没有道路和建筑,一片空白,没办法按照所需目标的轮廓标注多边形,而卫星地图模式清晰度非常差。
  2. 使用高德地图官方提供的地图快速生成工具,利用“添加标注”功能,把需要标注的区域标注好,然后点击“获取代码”按钮,找到features变量,这里面包含了我们想要的数据,但并不是GeoJSON格式,不过这对于程序员来说非常简单,只需要非常简单的处理,就能获取到上面代码中coordinates部分需要的数组,这里不再赘述。
  3. 使用这个GeoJSON在线编辑器,它的卫星地图比较清晰,但有三个缺点:1是可能需要科学上网,它自带3种地图模式,第一种不能显示。另外,地图缩放后,需要拖动一下地图,才能触发地图的加载。2是标注对于高德地图有所偏移,需要处理,一般是将获取到的经纬度统一加减一些比较小的数值,大约是在小数点后3、4位。3是经度的表示方法可能与高德地图不同,例如-243.63281249999997,需要如下处理:

    360 - Math.abs(-243.63281249999997)

  4. 当然,也可以使用坐标拾取器,一个点一个点地获取和填写coordinates数组。需要注意的是,必须要登录高德地图网站,才能获取精确的位置。

这里使用第二种方法,获取到故宫的外形如下:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "id": 10647,
            "properties": {},
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            116.39156454,
                            39.92287908
                        ],
                        [
                            116.40190578,
                            39.92322019
                        ],
                        [
                            116.40229494,
                            39.91337002
                        ],
                        [
                            116.39189816,
                            39.91294358
                        ],
                        [
                            116.39156454,
                            39.92287908
                        ]
                    ]
                ]
            },
            "bbox": [
                116.39156454,
                39.91294358,
                116.40229494,
                39.92322019
            ]
        }
    ],
    "gid": 7711
}

新建polygon.json,将上面的数据粘贴进去。

4、将数据添加到地图上

代码如下:

var geo = new Loca.GeoJSONSource({
    url: './polygon.json',
});

var pl = new Loca.PolygonLayer({
    zIndex: 120,
    opacity: 0.8,
    shininess: 10,
    hasSide: true,
});

pl.setSource(geo);

pl.setStyle({
    //设置不同面的颜色
    topColor: 'rgba(43,83,144,0.5)',
    sideTopColor: 'rgba(43,83,144,0.5)',
    sideBottomColor: 'rgba(43,83,144,0.5)',
    height: 2000, //物体的高度
    altitude: 0,
});

loca.add(pl);

这段代码非常简单。需要注意的是,高德的文档或者示例代码可能不一致,如果你遇到代码不起作用的情况,需要尝试改为 API 文档中的代码或者示例代码中的代码。

下面是显示效果:

1.png

这是某工厂的建筑:

2.png

除了三维多边形,还可以添加许多东西,详情参考Loca数据可视化API

End

这篇文章记录我感染奥密克戎的症状和感受。

我是在12月9日发烧的。当天上午,我在公司正常上班,忽然感觉感冒症状十分严重:流鼻涕、打喷嚏,头有点昏沉,并且感到有点心慌——每次开始发烧,我都会感到一阵阵心慌,好像非常着急的那种感觉。到了中午,我感觉坚持不下去了,于是跟公司请了假。出了公司,首先开车去一个核酸点做了个核酸,然后昏昏沉沉地回家了。

其实在这之前的两三天,我就曾经因为感冒休息了一天。当时真的没有想到自己会中招,只以为是普通的感冒。而经过那一天的休息后,还正常上了两天的班,直到12月9日。现在回想起来,我的整个病程,可能是感冒+奥密克戎,因为网上流传的奥密克戎症状,在我身上表现的不多。

当天到家之后马上测量体温,结果发现自己发烧了,体温大概37.5℃。这时除了发烧和有点头晕,没有其他感觉。晚上睡前再次测量体温,已经达到了38.5℃了,决定去药店买些布洛芬。虽然已经听说药品供应不足,但还是想碰碰运气。到了药店,说明来意,店员告诉我,布洛芬还有,但是不多了。除了布洛芬,还买了一袋感冒颗粒、一盒头孢克洛。

第二天跟前一天的情况基本一样,只是体温更高了,最高达到了39.3℃。服药情况:早晚两颗布洛芬,每天三次6袋感冒颗粒。

到了第三天,突然就退烧了,体温降到了37℃以下。感觉自己基本好了,于是去做了个核酸,如果是阴性就去上班。其实这时我还抱有侥幸心理,以为自己只是得了普通的感冒。

第二天核酸监测点打来电话,告知我核酸检测异常。在我们这里,核酸阳性不会上传到健康码。虽然二十一条、十条要求精准抗疫,但感觉实际上已经完全各安天命了。

轻松的状态持续了两天,这时候从开始发烧已经过去了4天。难道是我天赋异禀,就这就完事儿了?我甚至还洗了个澡。现在才知道,奥密克戎专治各种嘴硬和不服。从第5天开始,出现了严重的感冒症状:鼻子从来没有这样堵过,大脑昏沉,耳鸣、耳朵疼。擤鼻涕后,耳朵里有气,一直不消散,导致耳朵感觉胀痛,更要命的是,感觉肺部有点不适。其实我以前感冒的时候也会出现肺部不适,但是这次不一样,毕竟感染的是奥密克戎。虽然我知道抗生素对病毒没有效果,但万一自己真的是奥密克戎合并感冒,出现了呼吸道感染呢?并且我因为一些原因没有打疫苗……于是开始服用头孢克洛。当时真的是挺慌乱的。

就在这几天里,周围的人已经是应得尽得了。合租的其他几个屋子里,时不时传来咳嗽声。大街上的情形堪比封控,冷清寂寥,只有西北风卷起尘土飞扬。

又过了两天,嗅觉消失了。这个体验非常神奇,闻不到味道,吃什么都只能感受到最基本的咸、甜等,食物完全没有任何特有的香味。

故事到这里其实也就基本结束了。嗅觉3天后恢复了。感冒的症状逐渐减弱。到了19号,核酸结果已经转阴了。从开始发烧到转阴,一共经历了10天。如果算上潜伏期2天,那么病程一共12天。

今天,已经正常上班将近两个星期了。虽然楼上一个老人开始咳嗽,但路上的车辆行人已经大部分恢复了,公司园区内也恢复了车位难找的样子。但是,办公室的每个人,包括我在内,都多少有点遗留的症状,主要是咳嗽。我总是有痰咳不干净,很多年没有这样过了。

回想起这段经历,首先是感觉奥密克戎防不胜防。我根本不知道我是在哪中招的,即使戴着口罩也不管用。仔细回忆,菜市场有最大的嫌疑。然后是觉得庆幸。感谢国家在病毒毒性减弱的时候才放开,否则没有打疫苗的我,可能会承受更大的痛苦。我也没有刀片喉、全身酸痛无力、腰疼得要折这些症状,只是鼻塞了一两天。其次是感觉混乱。从来没有想过,一场疫情能够产生如此多的虚假信息和谣言,专家的话可信度下降,不同观点的群众被撕裂开来,各种政治势力甚至有外部势力参与到一场疫情中来。这不由得让人怀疑,疫情产生的原因,真的不简单。另外,也有很多失望。有集中力量办大事的优势,有高度理解和配合的群众基础,为什么在应对奥密克戎的时候如此拉胯?难道真的是被网上的舆论左右了政策吗?

这几天,好多知名人士都密集去世了,村里的老人也走了好多,比往年多得多。甚至公司群里有人透露,有平时身体很壮的年轻人也不幸去世了。

奥密克戎绝对不是感冒,无症状率很低是绝对的谎话。作为普通人,能做到的只能是尽可能加强自我防护,多准备一些必需品,保护家人尽量不被传染。至于纷繁复杂的各种信息,能不看就不看吧。你可能已经发现了,关于新冠的信息,几乎没有很明确的描述,都是“可能低于”、“无需过分担心”这样的废话。这个阶段网上信息鱼龙混杂,混乱至极,千万不要跟风和盲从,即使发布信息的是网红或者大V,也千万不要轻信,要保持清醒和冷静,从常识角度分析信息是否正确。要始终坚持基本目的:尽力保护好自己和身边的人。

另外,感染了奥密克戎后,千万要注意保暖,貌似这个病毒非常喜欢冷,环境温度变化,身体的症状会有明显的变化。

啥也不说,直接上链接:https://developer.mozilla.org/zh-CN/docs/Web/API/structuredClone
如果你懒得点,那只要知道下面的东西就行了。

全局的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝,说人话就是使用了 JavaScript 内部的一种算法深拷贝对象。这个内部的方法其实也是递归,它有一些限制,不能拷贝一些东西,这些距离咱们码农很远,一般不用考虑。如果你很好奇,结构化克隆算法的介绍在这里:

结构化克隆算法:
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm

返回值就是你想要的那个东西:新对象:

structuredClone 的语法如下:
structuredClone(value)
structuredClone(value, { transfer })

可选参数 transfer 是啥?是可转移对象数组。啥是可转移对象数组?大概是跟内存有关的数据,例如 arrayBuffer 之类的,一般情况下也用不到。介绍在这里:

可转移对象:
https://developer.mozilla.org/zh-CN/docs/Glossary/Transferable_objects

structuredClone 示例如下:

let obj = { x: 1 };
let newObj = structuredClone(obj);
console.log(obj === newObj); //false

END

svg标签既能设置widthheight属性,又能设置viewBox属性,而svg作为一个 html 标签,也能在 css 或者 style 中设置widthheight。这三者的关系是什么?

1、只有viewBox属性

这里有一个 svg 制作的鸡蛋黄(有点黄,可能是一个咸鸡蛋黄),代码如下:

<style>
    #svg{
        background-color:antiquewhite;
    }
</style>

<svg id="svg" viewBox="0 0 200 200">
    <circle cx="100" cy="100" r="100" fill="darkorange"></circle>
</svg>

效果如下:

1.png

可以看到,半径是100的圆,填满了整个svg,因为svgviewBox的长宽都是200。而svg标签作为 html 元素,又填满了整个页面宽度,并不是200*200的尺寸。

下面我们改变一下viewBox的大小:

...viewBox="0 0 100 100"

效果是这样的:

1 (1).png

viewBox变大一些呢?

...viewBox="0 0 300 300"

效果是这样的:

1 (2).png

现在这个鸡蛋黄已经不能占满整个 svg “画布”了。如果修改鸡蛋黄的圆心坐标为viewBox的中心点,是不是会让鸡蛋黄居中呢?

<circle cx="150" cy="150"...>

确实居中了:

1 (3).png

看来圆的坐标系是viewBox

2、给 svg 标签添加widthheight属性

将代码恢复到最原始的样子,并进行下面的修改:

<svg height="100" width="100"...

变化如下:

1 (4).png

svg 作为 html 元素,长宽都变成了100px,但鸡蛋黄还是占满整个svg标签。看来svgwidthheight属性是用来控制svg作为 html 元素的宽高的,不会影响svg里面的形状等元素的大小。

3、设置 css 宽高

在上面代码的基础上,再添加下面的代码:

...
#svg{
    height: 300px;
    width: 300px;
    ...
}
...

这时候有了下面这种变化:

1.png

svg 元素的宽高变成了 css 中设置的300px

4、总结

根据上面的表现,可以得出下面的结论:

  1. 不提各种坐标,简单理解,可以将viewBox看作是svg画布大小。画布变大了,里面的形状当然就显得小了;如果画布变小了,就有可能将里面的形状裁切。这跟 Photoshop 中的图像 - 画布大小命令表现是一样的。
  2. svg 的widthheight属性设置的是svg这个 html元素 的大小。因为 svg 是矢量图形,所以 svg 的viewBox,包括viewBox中的内容会等比缩放,但并不会变形。当然,widthheight要和viewBox等比例,否则会出现裁切
  3. css 中的widthheight会覆盖 svg 标签本身的widthheight属性,也就是 css 的宽高具有更高的优先级。

在制作svg图形时,可以任意定义viewBox大小,并在其中放置各种形状,这些形状(包括文字等)的定位都相对viewBox。而svg实际应用后的大小,取决于widthheight属性,或者css样式。

是不是非常简单?


End