deckGL自定义图层学习笔记

1.自定义图层

在这里插入图片描述

当使用DeckGL提供的图层还无法满足需求时(https://deck.gl/docs/api-reference/layers),可能就需要自定义图层了。在DeckGL中有常见的三种自定义图层的方式

  • 创建复合层(composite layers.)——复合层是一种可以创建其他层的特殊层
  • 子类层(Subclass a layer)——子类层是通过子类化其他层创建的新层。这允许开发人员重用现有层的所有接口和实现
  • 从0开始构建图层层——也就是使用WebGL自定义绘制图层绘制,这就意味着你可以完全控制图层生命周期,可以管理自己的模型并直接操作 WebGL 上下文,当然难度也是最大的…(感觉上面两个应用场景也不少了随着DeckGL的版本的升级提供了挺多常用的layer)

[1]图层的生命周期

为了理解每个 deck.gl Layer的子类都可以定义在其生命周期中的特定点调用的特定方法

  • initializeState - 对于刚刚添加的layer进行初始化时(初始化进发生一次),该[layer.updateState()](https://deck.gl/docs/api-reference/core/layer#updatestate)生命周期函数将会被调用
  • Updating-layer发生变化(如修改属性、数据等)时调用 layer.shouldUpdateState() 以确定图层是否需要更新。
    • 如果确实需要更新则在Layer渲染前调用 layer.updateState() ,通常在这一阶段通过调用[state.attributeManager.invalidate](https://deck.gl/docs/api-reference/core/attribute-manager#invalidate) 来重新计算attribute 和调用model.setUniforms来更新uniforms 变量/默认情况下,当 props.data 更改时,所有attribute都会失效并重新计算
  • Rendering - 渲染发生在每个渲染周期,将图层绘制到 WebGL 上下文。
    • 对于primitive layers, [layer.draw()](https://deck.gl/docs/api-reference/core/layer#draw)函数在此阶段将被调用
  • Picking - 当指针移过或单击 deck.gl 画布时发生
    • 拾取图层时,将调用 layer.getPickingInfo() 以生成有关已拾取内容的信息对象。然后将此对象传递给图层的 onHover 或 onClick 回调。
  • Finalization - 发生在每个被移除的层上,即来自上一个渲染周期的层,其 id 与当前周期中的任何层都不匹配。 layer.finalizeState() 在对该层的状态的引用被释放之前被调用

[2]复合层

复合层是一种特殊的图层,可以通过组合现有层来构建新的层(顾名思义:通过组合多个层来构成一个层)如GeoJsonLayer就是复合层

复合层中分为适配层Adaptor Layers和集合层Collection Layers

定义一个继承于CompositeLayer的layer

Adaptor Layers

使用复合“适配”层更改现有层的接口和行为通常很方便,而不是修改层本身(例如 S2Layer 是 PolygonLayer 之上的简单适配层)

简单的说也就是,根据现有层添加一些适配(如数据)

对于复合层主要的意义我估计就是使用相同的数据,来给原来的多个层使用渲染,然后组合为一个层

应用举例:

  • LASPointCloudLayer为Adaptor Layers的适配层
  • TopoJSONLayer为GeoJsonLayer的适配层
  • TextLayer, HexagonLayer, CPUGridLayer 等都为composite “adapter” layers.
Collection Layers

通常,一些复杂的可视化层是由使用一组具有通用属性的多个层合成的。例如:

  • 例如NodeLayer图层由ScatterplotLayer、TextLayer组合而成
  • GeoJsonLayer 、PolygonLayer 为集合层

创建一个集合层的优势:

  • 可以将处理特定数据格式或可视化配置的复杂代码收集到一个类中
  • 通过跨层共享相同的对象/缓冲区来提高内存使用率。复合层将管理数据源并将其向下传递给多个层,而不是每个子层加载和存储自己的原始数据副本。

例如:实现一个有icon和text的layer(通过组合IconLayer和TextLayer)

import {CompositeLayer, IconLayer, TextLayer} from 'deck.gl';class LabeledIconLayer extends CompositeLayer {//一个组合图层需要实现renderLayers()方法并返回一个子图层数组renderLayers() {return [new IconLayer({//由于复合层不直接绘制到画布上,它通过设置其子层的props来控制渲染结果,而且子图层并不知道复合图层的,所以需要将复合图层的props映射到子图层相应的propsid: `${this.props.id}-icon`,data: this.props.data,iconAtlas: this.props.iconAtlas,iconMapping: this.props.iconMapping,getPosition: this.props.getPosition,getIcon: this.props.getIcon,getSize: this.props.getIconSize,getColor: this.props.getIconColor//为了在需要重新计算访问器时使 updateTriggers 起作用updateTriggers: {getPosition: this.props.updateTriggers.getPosition,getIcon: this.props.updateTriggers.getIcon,getSize: this.props.updateTriggers.getIconSize,getColor: this.props.updateTriggers.getIconColor}}),//由于复合层不直接绘制到画布上,它通过设置其子层的 props 来控制渲染结果new TextLayer({id: `${this.props.id}-label`,data: this.props.data,fontFamily: this.props.fontFamily,fontWeight: this.props.fontWeight,getPosition: this.props.getPosition,getText: this.props.getText,getSize: this.props.getTextSizegetColor: this.props.getTextColor//为了在需要重新计算访问器时使 updateTriggers 起作用updateTriggers: {getPosition: this.props.updateTriggers.getPosition,getText: this.props.updateTriggers.getText,getSize: this.props.updateTriggers.getTextSize,getColor: this.props.updateTriggers.getTextColor}})];}}
LabeledIconLayer.layerName = 'LabeledIconLayer';
//定义组合图层属性
LabeledIconLayer.defaultProps = {// Shared accessorsgetPosition: {type: 'accessor', value: x => x.position},// Icon propertiesiconAtlas: null,iconMapping: {type: 'object', value: {}, async: true},// Icon accessorsgetIcon: {type: 'accessor', value: x => x.icon},getIconSize: {type: 'accessor', value: 20},getIconColor: {type: 'accessor', value: [0, 0, 0, 255]},// Text propertiesfontFamily: DEFAULT_FONT_FAMILY,fontWeight: DEFAULT_FONT_WEIGHT,// Text accessorsgetText: {type: 'accessor', value: x => x.text},getTextSize: {type: 'accessor', value: 12}getTextColor: {type: 'accessor', value: [0, 0, 0, 255]}
}

子层 id 必须根据其父层的 id 动态生成,否则当有多个 LabeledIconLayers 实例时,它们的子层 id 将发生冲突。

例如还可以实现labeldGeoJsonLayer

小结

构建复合层的通用步骤为(和SubClass差不多)

  • 定义面向用户的api(继承你要使用的图层的defaultProps,然后自定义添加一些而外的props
    定义renderLayers()渲染逻辑,因为CompositeLayer并不是开箱即用的层,而是需要使用renderLayers()来组合其他层的渲染逻辑
  • 数据装换(通常想要渲染的数据和Layer要求的数据格式是不一样的),那么就可以需要使用CompositeLayer的数据解析逻辑“适配”你的数据格式,使用CompositeLayer的另外的好处是它允许多个子层共享同一个数据集,这就大程度的提升了数据传输性能。
    • 使用钩子函数updateState()来处理当数据发送变化时,在该函数下对数据进行处理,并会自动将处理结果存储在复合层的状态中。这样它也可以被子层访问。

例如根据geoJsonLayer定义一个CompositeLayer(给几何添加一些文本标注)

const {CompositeLayer, GeoJsonLayer, TextLayer} = deck;
//定义面向用户的prop API
const defaultProps = {//继承GeoJsonLayer props API...GeoJsonLayer.defaultProps,//获取每个feature的labelgetLabel: {type: 'accessor', value: x => x.text},// 每个feature的label的大小getLabelSize: {type: 'accessor', value: 32},// 每个feature的label的颜色getLabelColor: {type: 'accessor', value: [0, 0, 0, 255]},// label始终面向相机billboard: true,// label大小单位labelSizeUnits: 'pixels',// Label background colorlabelBackground: {type: 'color', value: null, optional: true},// Label fontfontFamily: 'Monaco, monospace'
}class LabeledGeoJsonLayer extends CompositeLayer {//图层的生命周期函数,图层在初始化时被调用,对geojson数据做处理,提取取label文本数据updateState({changeFlags}) {const {data} = this.props;if (changeFlags.dataChanged && data) {const labelData = (data.features || data).flatMap((feature, index) => {const labelAnchors = getLabelAnchors(feature);return labelAnchors.map(p => this.getSubLayerRow({position: p}, feature, index));});this.setState({labelData});}}
//渲染逻辑(使用同一个数据将GeoJsonLayer和TextLayer的渲染逻辑组合到一起)renderLayers() {const {getLabel,getLabelSize,getLabelColor,labelSizeUnits,labelBackground,billboard,fontFamily} = this.props;return [new GeoJsonLayer(this.props, this.getSubLayerProps({id: 'geojson'}), {data: this.props.data}),new TextLayer(this.getSubLayerProps({id: 'text'}), {data: this.state.labelData,billboard,sizeUnits: labelSizeUnits,backgroundColor: labelBackground,getPosition: d => d.position,getText: this.getSubLayerAccessor(getLabel),getSize: this.getSubLayerAccessor(getLabelSize),getColor: this.getSubLayerAccessor(getLabelColor)})];}
}
LabeledGeoJsonLayer.layerName = 'LabeledGeoJsonLayer';
LabeledGeoJsonLayer.defaultProps = defaultProps;const layer = new LabeledGeoJsonLayer({id: `countries-${Date.now()}`,data,filled: false,billboard: false,getLineColor: [180, 180, 180],getLabel: f => f.properties.name,getLabelSize: f => Math.pow(2, Math.log10(turf.area(f))) * 20,getLabelColor: [0, 64, 128],labelSizeUnits: 'meters',lineWidthMinPixels: 1
});deckgl.setProps({layers: [layer]});

[3]Subclassed Layers

deck.gl Layer被设计为易于扩展以添加功能。子类化允许重新定义层生命周期方法以及顶点和/或片段着色器。

扩展子类的示例:https://observablehq.com/d/ca5bcbd3d740693b

如果层中缺少一个小功能,子类化通常是添加它的好方法,常见的有:

通常写一个Subclassed Layers分为如下步骤:

  • 设计面向用户的API
    • To create a new layer class, just extend the Layer class from @deck.gl/core. By default, the layer does not do anything.
    • 用户通过传递prop给自定义的图层类的构造函数,用来告诉图层如何渲染给定的数据集,通常在defaultProps中扩展添加新的props给用户
    • 其实也就是是继承类后给静态成员等添加一些属性,用来在调用该类是根据数据添加一些option
  • 设定自定义attribute和uniform(定义数据怎么传的给的着色器变量的名字)
    https://observablehq.com/@pessimistress/deck-gl-tutorial-subclassing-a-layer
    • 可以通过定义attribute(每个对象特有)和uniform(所有对象共享)来使shader能够拿到拿到用户提供的数据
    • 默认情况下每个Layer都有一个AttributeManager来将要传递给attribute的数据上传到webgl缓冲区中(this.getAttributeManager())
    • 要自定义attribute的话,可以调用this.attributeManager.add()this.attributeManager.addInstanced(),然后将其添加到Layer的生命周期initializeState()
    • 设置自定义uniform则可以通过this.state.model.setUniforms()来设定
  • shader注入
    • deck.gl 的图层实现了标准的着色器hook,这样一来就可以在着色器中一些常见的地方添加shader代码了
Overriding Attribute Calculation

可以创建attribute变量,以供后面添加着色其代码时传输给着色其,在生命周期函数initializeState()中添加

export default class MultiColorPathLayer extends PathLayer {initializeState() {super.initializeState();this.getAttributeManager().addInstanced({instanceFrequency: {size: 1,accessor: 'getFrequency',defaultValue: 1},})}
}
Overriding Shaders

着色器注入语句:

  • **vs:#decl:**向顶点着色器顶部(声明)注入代码
  • **vs:#main-start:**向顶点着色器主函数开始的地方注入代码
  • **vs:#main-end:**向顶点着色器主函数结束的地方注入代码
  • **vs:DeckGL_FILTER_SIZE:**顶点着色器中的一个函数,用于操纵几何体的大小,在投影计算前注入
    • DECKGL_FILTER_SIZE(inout vec3 size, VertexGeometry geometry)
  • **vs:DeckGL_FILTER_GL_POSITION:**向顶点着色器最终坐标计算上注入重写代码:顶点着色器中的一个函数,用于操作当前顶点的投影位置。投影后调用
    • DECKGL_FILTER_GL_POSITION(inout vec4 position, VertexGeometry geometry)
  • **vs:DeckGL_FILTER_COLOR:**向顶点着色器注入顶点颜色重写代码。
  • **fs:#decl:**向片元着色器注入声明代码。
  • **fs:#main-start:**向片元着色器主函数开始的地方注入代码。
  • **fs:#main-end:**向片元着色器主函数结束的地方注入代码。
  • **fs:DeckGL_FILTER_COLOR:**向片元着色器注入最终的颜色重写代码。
Defining Additional Uniforms

额外的Uniforms传递给自定义着色器的最佳方法是重写 draw() 方法:

Defining Additional Attributes
Layer Extensions

对于CompositeLayer通常采用添加图层扩展而不直接继承为子类图层去添加一个功能或效果,因为CompositeLayer通常是由几个图层组合而成的,所以在CompositeLayer继承进行添加自定义效果时,要确保在每个图层都生效就要通过对每个子图层进行子类扩展并添加相同的内容,这样就会比较麻且代码重复了,而图层扩展直接给CompositeLayer图层添加一个扩展变可以对其CompositeLayer的所有组成Layer都生效了。

有时我们需要对多个层进行子类化以添加类似的功能。层扩展是一种概括、重用和共享子类层代码的方法

[4]Primitive Layers

直接扩展Layer基类来自定义一个layer

[5]shader注入

着色器注入语句:

  • **vs:#decl****:**向顶点着色器顶部(声明)注入代码
  • **vs:#main-start****:**向顶点着色器主函数开始的地方注入代码
  • **vs:#main-end****:**向顶点着色器主函数结束的地方注入代码
  • **vs:DeckGL_FILTER_SIZE****:**顶点着色器中的一个函数,用于操纵几何体的大小,在投影计算前注入
    • DECKGL_FILTER_SIZE(inout vec3 size, VertexGeometry geometry)
  • **vs:DeckGL_FILTER_GL_POSITION****:**向顶点着色器最终坐标计算上注入重写代码:顶点着色器中的一个函数,用于操作当前顶点的投影位置。投影后调用
    • DECKGL_FILTER_GL_POSITION(inout vec4 position, VertexGeometry geometry)
  • **vs:DeckGL_FILTER_COLOR****:**向顶点着色器注入顶点颜色重写代码。
  • **fs:#decl****:**向片元着色器注入声明代码。
  • **fs:#main-start****:**向片元着色器主函数开始的地方注入代码。
  • **fs:#main-end****:**向片元着色器主函数结束的地方注入代码。
  • **fs:DeckGL_FILTER_COLOR****:**向片元着色器注入最终的颜色重写代码。

2.API使用

此部分为对官方文档的翻译

[1]Using Layers

  • 图层是deckGL中一个核心的概念, deck.gl 图层是一种打包的可视化类型,它采用一组数据,将每个数据与位置、颜色、拉伸等相关联,并将它们呈现在地图上。DeckGL定义了封装了很多图层,同时也允许通过编写着色其自定义图层
  • DeckGL将按顺序渲染渲染图层

[2]add Interactivity

  • 可以在实例化Deck实例时通过initialViewState配置相机位置

[3]坐标系统CS

把地理坐标中各种投影坐标系看做渲染引擎中的世界空间即可

基本概念
  • 世界空间(World space)

数据集的世界坐标系。它通常由数据源决定,例如生成数据的设备,以及存储在磁盘或云数据仓库中的格式

  • 公共空间(Common space)

为了将来自不同世界空间的数据正确地组合在一起,deck.gl 将它们转换为公共空间(Common space),一个统一的中间层 3D 空间,它是一个右手笛卡尔坐标系。一旦位置位于公共空间中,就可以安全地使用标准线性代数将它们添加、减去、旋转、缩放和拉伸为 3D 向量。这是 deck.gl 图层中所有几何处理的基础。

世界空间和公共空间之间的转换在 deck.gl 文档中称为“project”(世界空间到公共空间)和“unproject”(公共空间到世界空间),由世界空间规范(例如 WGS84)和投影方式(例如 Web Mercator)控制的过程。投影是作为 deck.gl 核心的一部分实现的

  • 屏幕空间

也就是像素坐标,对于给定的数据集,公共空间中的位置通常不会随着用户交互而改变,而它们在屏幕空间中的外观会随着用户平移、缩放和旋转相机而频繁变化

3.源码解读

其源码结构类似于常见的常见的地图引擎,很多继承类的关系。

MapboxLayer其实是对mapbox中的customLayer的进一步封装,实例化其实就是customLayer图层

[1]view

  • View 实例包装了相机的“硬配置”。一旦定义,就不需要经常更改
  • DeckGL允许调用多同时使用多个视图

[2]View State

  • View 实例必须与 viewState 对象结合使用
  • View State对象描述了 View 实例的状态。视图状态对象在运行时定义视图的临时属性,如相机位置、方向、缩放等。如果视图是交互式的,每次用户平移/旋转/缩放时,视图状态都会更新以反映改变

[3]Viewport

  • 视口实例是相机本身,是从 View 实例及其 viewState 中“解析”出来的。它处理数学运算,例如坐标投影/反投影、投影矩阵的计算以及着色器所需的其他 GLSL uniforms
  • 每当 viewState 更新时,view都会在后台创建一个新的viewport,通常,deck.gl 用户不需要直接使用视口Viewport。在某些用例中,Viewport 实例提供的 JavaScript 函数可以方便地投影和取消投影坐标。

坐标变换

在DeckGL绘制地理数据时因为地理数据的坐标系是很多种类的,所以在实际绘制时需要先对地理坐标进行转换,需要将经纬度为单位的地理进行投影为米为单位的,然后在进行类似于常见渲染引擎中的变换,如观测变换、投影变换等。

而因为地理数据的地理坐标的坐标系的种类是非常多的,所以为了方便计算和提升性能,大多数WebGIS引擎(如DeckGL、openlayers、mapbox)都默认只支持一种地理坐标系和一种投影坐标系,也就是WGS84 WKID=4326和web墨卡托 WKID=3857。

所以如果要使用其他坐标系的地理数据(如地方坐标系)时,就比较麻烦了,可以先查看有没有相应的API提供,如没有则自己写或者调用网络上其他开源的工具(如proj)来对坐标进行装换计算了。

Layer

Layer 类是所有 deck.gl Layers的基类,它对许多Layers提供了一些基础属性

MapboxLayer

在DeckGL中MapboxLayer类位于源码目录中的modules/mapbox/src/mapbox-layers.ts

//实现了MapBox的CustomLayerInterface接口
export default class MapboxLayer<LayerT extends Layer> implements CustomLayerInterface {id: string;type: 'custom';renderingMode: '2d' | '3d';map: Map | null;deck: Deck | null;props: MapboxLayerProps<LayerT>;constructor(props: MapboxLayerProps<LayerT>) {if (!props.id) {throw new Error('Layer must have an unique id');}this.id = props.id;this.type = 'custom';this.renderingMode = props.renderingMode || '3d';this.map = null;this.deck = null;this.props = props;}/* deck对onAdd方法的实现主要是创建deck实例,并添加设置一些props给deck实例,然后将 */onAdd(map: Map, gl: WebGLRenderingContext): void {获取mapbox的map实例,主要this.map = map;//这里通过Layer构建this.deck = getDeckInstance({map, gl, deck: this.props.deck});addLayer(this.deck, this);}onRemove(): void {if (this.deck) {removeLayer(this.deck, this);}}setProps(props: MapboxLayerProps<LayerT>) {// id cannot be changedObject.assign(this.props, props, {id: this.id});// safe guard in case setProps is called before onAddif (this.deck) {updateLayer(this.deck, this);}}render() {drawLayer(this.deck!, this.map!, this);}
}

因为上面代码是基于Mapbox 代码为

下面以MVTLayer为里继续深入阅读源码,以了解其渲染机制:

安装说明

安装整个deckGL库

npm install deck.gl --save

选择性安装模块,直接安装DeckGL库的话其包含如下库

  • @deck.gl/core
  • Core module that handles the WebGL rendering pipeline, data management, and user interaction
    • @deck.gl/layers
  • Primitive layers that are the building blocks of all visualizations
    - @deck.gl/aggregation-layers - Advanced layers that aggregate data into alternative representations, e.g. heatmap, contour, hex bins, etc.
    - @deck.gl/geo-layers - Additional layers that handle geospatial use cases and GIS formats.
    - @deck.gl/mesh-layers - Additional layers that render 3D meshes and scene graphs.
    • @deck.gl/json - Declarative interface that supports specifying deck.gl layers and views using a JSON format.
    • @deck.gl/mapbox - An integration with the Mapbox custom layer API.
    • @deck.gl/react - React wrapper of deck.gl.
    • @deck.gl/test-utils - Testing utilities.

为了减小包的大小可以按需选择上面的包进行安装

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/153051.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

建立数据科学基础设施的绝佳指南 数据工程师都该人手一册

《Effective数据科学基础设施》由Netflix工程师Ville Tuulos撰写&#xff0c;以Metaflow为对象&#xff0c;介绍了数据科学所需要的基础设施&#xff0c;囊括数据准备、特征工程、模型训练、模型部署、服务和持续监控等环节。Metaflow专注于构建生产流程&#xff0c;更适合具有…

最新AI创作系统源码ChatGPT网站源码V2.6.3/支持Midjourney绘画/支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统&#xff0c;支持OpenAI GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Chat…

想要开发一款游戏, 需要注意什么?

开发一款游戏是一个复杂而令人兴奋的过程。游戏开发是指创建、设计、制作和发布电子游戏的过程。它涵盖了从最初的概念和创意阶段到最终的游戏发布和维护阶段的各个方面。 以下是一些需要注意的关键事项&#xff1a; 游戏概念和目标&#xff1a; 确定游戏开发的核心概念和目标…

小视频APP源码选择指南:挑选最适合你的开发框架

在如今蓬勃发展的小视频APP行业中&#xff0c;源码的选择是打造一款成功应用的关键步骤。然而&#xff0c;面对众多开发框架的选择&#xff0c;如何挑选最适合你的小视频APP源码呢&#xff1f;作为这一领域的专家&#xff0c;我将为你提供一份详尽的指南&#xff0c;助你在源码…

nginx-proxy反向代理缓存

介绍&#xff1a; 反向代理缓存&#xff0c;类似于动静分离&#xff0c;即通过nginx代理服务器根据客户端发送的url请求&#xff0c;去后台服务器获取数据&#xff0c;将静态数据缓存到nginx代理服务器上&#xff0c;并配置有过期时间&#xff0c;当客户端下次以相同的url请求…

LVS+Keepalived 高可用集群负载均衡

一.keepalived介绍 1.1.Keepalived实现原理 由多台路由器组成一个热备组&#xff0c;通过共用的虚拟IP地址对外提供服务。 每个热备组内同时只有一台主路由器提供服务&#xff0c;其他路由器处于冗余状态。 若当前在线的路由器失效&#xff0c;则其他路由器会根据设置…

【Python查找算法】二分查找、线性查找、哈希查找

目录 1 二分查找算法 2 线性查找算法 3 哈希查找算法 1 二分查找算法 二分查找&#xff08;Binary Search&#xff09;是一种用于在有序数据集合中查找特定元素的高效算法。它的工作原理基于将数据集合分成两半&#xff0c;然后逐步缩小搜索范围&#xff0c;直到找到目标元素…

ChatGPT是如何产生心智的?

一、前言 - ChatGPT真的产生心智了吗&#xff1f; 来自斯坦福大学的最新研究结论&#xff0c;一经发出就造成了学术圈的轰动&#xff0c;“原本认为是人类独有的心智理论&#xff08;Theory of Mind&#xff0c;ToM&#xff09;&#xff0c;已经出现在ChatGPT背后的AI模型上”…

[极客大挑战 2020]Roamphp2-Myblog - 伪协议+文件上传+(LFIZIP)||(LFIPhar)【***】

[极客大挑战 2020]Roamphp2-Myblog 1 解题流程1.1 分析1.2 解题1.3 中场休息——再分析1.3.1 浅层分析1.3.2 难点疑惑1.3.3 深度分析 1.4 重整旗鼓——再战1.4.1 解法一&#xff1a;zip伪协议1.4.2 解法二&#xff1a;phar伪协议 2 总结展望 1 解题流程 1.1 分析 1、点击logi…

Linux——指令初识(二)

Linux下基本指令 前言一、时间相关的指令二、Cal指令三、find指令四、grep指令五、sort指令六、uniq指令七、.zip/unzip指令八、.tar指令九、uname –r指令十、重要的几个热键[Tab],[ctrl]-c, [ctrl]-d十一、关机总结 前言 linux的学习开始啦&#xff01; 今天我们继续来认识指…

零基础自学考证HCIE分享,附零基础HCIE学习路线

最近有些粉丝问我&#xff0c;能不能自学华为认证网络工程师HCIE&#xff1f; 我的回答是&#xff1a;能&#xff0c;但是很难。 据不完全统计&#xff0c;考上HCIE的人群中自学占比10%左右。为什么会这么低呢&#xff0c;下面就来给大家说考HCIE自学会遇到的一些困难。 首先&…

Android约束布局ConstraintLayout的Guideline,CardView

Android约束布局ConstraintLayout的Guideline&#xff0c;CardView <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:a…

【java学习】一维数组(9)

文章目录 1. 一维数组声明2. 一维数组初始化3. 数组元素的引用4. 数组元素的默认初始化 1. 一维数组声明 声明方式&#xff1a; type var[] 或 type[] var 例如&#xff1a; int a[]; int[] a1; double b[]; Mydate[] c; //对象数组2. 一维数组初始化 动态初始化&#xf…

VMware和别的服务器 ,组建局域网那些事 。

利用VMware &#xff0c;实现组件局域网、有可能会受限于WiFi&#xff08;路由器&#xff09; 。 通常不会&#xff0c;除非做了网关设置 相关知识&#xff1a; 禁用局域网隔离&#xff08;LAN Isolation&#xff09;&#xff1a; 某些路由器提供了一个选项&#xff0c;允许您禁…

【面试算法——动态规划 21】不同的子序列(hard) 通配符匹配(hard)

115. 不同的子序列 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 链接&#xff1a;&#xff1a;https://leetcode.cn/problems/distinct-subsequences/ 示例 1&#xff1a; 输入&#xff1a;s “rab…

【微服务】八. 统一网关gateway

8.1 网关作用介绍 网关功能&#xff1a; 身份认证和权限校验服务路由、负载均衡请求限流 网关的技术实现 在SpringCloud中网关的实现包括两种&#xff1a; gatewayzuul Zuul是基于Servlet的实现&#xff0c;属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的Web…

“元创新·智生成” 第15届企业数智化学习大会公布嘉宾阵容

2023年是AIGC爆发年&#xff0c;与AI相关的创新应用迅速向各行各业渗透。 在企业培训领域&#xff0c;数字人、元宇宙等正逐渐成为企业在开展人才发展、业务培训等工作的工具&#xff0c;其高效、便捷、在线化、场景化等优势受到企业的热捧。在需求的推动下&#xff0c;企业培…

springboot整合pi支付开发

pi支付流程图&#xff1a; 使用Pi SDK功能发起支付由 Pi SDK 自动调用的回调函数&#xff08;让您的应用服务器知道它需要发出批准 API 请求&#xff09;从您的应用程序服务器到 Pi 服务器的 API 请求以批准付款&#xff08;让 Pi 服务器知道您知道此付款&#xff09;Pi浏览器向…

【Java 进阶篇】CSS语法格式详解

在前端开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;用于控制网页的样式和布局。了解CSS的语法格式是学习如何设计和美化网页的关键。本文将深入解释CSS的语法格式&#xff0c;包括选择器、属性和值等基本概念&#xff0c;同时提供示例代码以帮助初学者更好地理解。…

微信小程序点单左右联动的效果实现

微信小程序点单左右联动的效果实现 原理解析&#xff1a;   点击左边标签会跳到右边相应位置&#xff1a;点击改变rightCur值&#xff0c;转跳相应位置滑动右边&#xff0c;左边标签会跳到相应的位置&#xff1a;监听并且设置每个右边元素的top和bottom&#xff0c;再判断当…