当前位置: 首页 > news >正文

react-grid-layout 实现原理介绍

简介

React-grid-layout 是一个基于 React 的网格布局库,它可以帮助我们轻松地在网格中管理和排列组件。它提供了一个直观的 API,可以通过配置网格的大小、列数和组件的位置来实现复杂的布局,还支持对网格中组件的拖拽和重新排列。

实现

诉求

  • 绘制:编排之后,可以根据配置对页面进行正确的绘制

  • 拖拽:支持位置调整,拖拽之后,调整对应配置参数

  • 缩放:支持大小调整,缩放之后,调整对应配置参数

  • 自动调整:当配置有错误、拖拽时有碰撞时,进行自动调整;布局空间的自动优化

示例:

https://react-grid-layout.github.io/react-grid-layout/examples/0-showcase.html

使用方式

const layout = [{ i: 'a', x: 0, y: 1, w: 1, h: 1, static: true },{ i: 'b', x: 1, y: 0, w: 3, h: 2 },{ i: 'c', x: 4, y: 0, w: 1, h: 2 },
];<GridLayoutlayouts={layout}margin={[10, 10]}containerPadding={[10,10]}cols=12rowHeight=150width=1200style={{}}
>{layout.map((item) => (<div key={item.i} className="item-style"><div>{item.i}</div><div>xy{item.x}/{item.y}</div><div>wh{item.w}/{item.h}</div></div>))}
</GridLayout>

绘制

容器

高度计算

const containerHeight = () => {// autoSize的情况下,不计算容器高度    if (!this.props.autoSize) { return; }// 底部坐标, 获取布局中y+h的最大值,即为容器的最大坐标h值    const nbRow = bottom(this.state.layout); const containerPaddingY = this.props.containerPadding ? this.props.containerPadding[1] : this.props.margin[1];// 计算包含margin/padding的真实高度  return ${nbRow * this.props.rowHeight + (nbRow - 1) * this.props.margin[1] + containerPaddingY * 2}px;
}

compact布局

如果直接安装上述的计算方式绘制,可能由于配置的问题,会带来页面的浪费。因此react-grid-layout会默认对纵向空间进行压缩,以避免空间的浪费,也支持设置为横向压缩,或者不压缩。在压缩过程中,很关键的一部分在于对模块间碰撞的处理。

function compact(layout, compactType, cols) {// 对静态模块不进行压缩const compareWith = getStatics(layout); // 排序 horizontal | verticalconst sorted = sortLayoutItems(layout, compactType); const out = Array(layout.length); for (let i = 0, len = sorted.length; i < len; i++) {let l = cloneLayoutItem(sorted[i]);if (!l.static) {l = compactItem(compareWith, l, compactType, cols, sorted);compareWith.push(l);}// 放回正确的位置out[layout.indexOf(sorted[i])] = l;// 在处理冲突的时候设置flagl.moved = false;}return out;
}// 压缩方法
function compactItem(compareWith: Layout,l: LayoutItem,compactType: CompactType,cols: number,fullLayout: Layout
): LayoutItem {const compactV = compactType === "vertical";const compactH = compactType === "horizontal";if (compactV) {// 垂直方向压缩,静态元素所需容器的高度 与 当前元素的纵向起始位置l.y = Math.min(bottom(compareWith), l.y);while (l.y > 0 && !getFirstCollision(compareWith, l)) {l.y--;}} else if (compactH) {while (l.x > 0 && !getFirstCollision(compareWith, l)) {l.x--;}}// 处理碰撞let collides;while ((collides = getFirstCollision(compareWith, l))) {if (compactH) {resolveCompactionCollision(fullLayout, l, collides.x + collides.w, "x");} else {resolveCompactionCollision(fullLayout, l, collides.y + collides.h, "y");}// 水平方向不超过最大值if (compactH && l.x + l.w > cols) {l.x = cols - l.w;l.y++;}}// 对上述的y--,x--做容错处理,确保没有负值l.y = Math.max(l.y, 0);l.x = Math.max(l.x, 0);return l;
}function getFirstCollision(layout: Layout,layoutItem: LayoutItem
): ?LayoutItem {for (let i = 0, len = layout.length; i < len; i++) {// collides方法: // 当前节点 或者节点出现在另一个节点的上方(下边界 > 其他节点的起始位置)/下方/右侧/左侧if (collides(layout[i], layoutItem)) return layout[i];}
}function resolveCompactionCollision(layout: Layout,item: LayoutItem,moveToCoord: number,axis: "x" | "y"
) {const sizeProp = heightWidth[axis]; // x: w, y: hitem[axis] += 1;const itemIndex = layout.map(layoutItem => {return layoutItem.i;}).indexOf(item.i);// 至少要加1个位置的情况下,其下一个元素开始判断,是否与元素碰撞for (let i = itemIndex + 1; i < layout.length; i++) {const otherItem = layout[i];// 静态元素不能动if (otherItem.static) continue;// 如果在当前元素的下方的话 breakif (otherItem.y > item.y + item.h) break;if (collides(item, otherItem)) {resolveCompactionCollision(layout,otherItem,// moveToCoord想相当于新的ymoveToCoord + item[sizeProp],axis);}}item[axis] = moveToCoord;
}

看一下紧凑布局的实际效果

const layout = [{ i: "a", x: 0, y: 0, w: 5, h: 5 },{ i: "b", x: 7, y: 6, w: 3, h: 2, minW: 2, maxW: 4, static: true },{ i: "c", x: 0, y: 10, w: 12, h: 2 },{ i: "d", x: 2, y: 16, w: 8, h: 2 },{ i: "e", x: 7, y: 9, w: 3, h: 1 }]; // 高度 (16 + 8) * 60 + 23 * 10 + 2 * 10 = 1690

compact调整之后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7UCuxkBr-1693221682629)(https://tech-proxy.bytedance.net/tos/images/1693221360491_d79de77167d3c591075cdc27711f3ed8.png)]

看一下碰撞检测之后的处理结果

allowOverlap | preventCollision

**allowOverlap 为true时,**可以允许覆盖/重叠,并且不会对布局做压缩处理。

**preventCollision为true时,**阻止碰撞发生,在拖拽/缩放时,会有其作用

容器绘制


render() {const { className, style, isDroppable, innerRef } = this.props;//样式合并const mergedClassName = classNames(layoutClassName, className);const mergedStyle = {height: this.containerHeight(),...style,};return (<divref={innerRef}className={mergedClassName}style={mergedStyle}// 拖拽功能onDrop={isDroppable ? this.onDrop : noop}onDragLeave={isDroppable ? this.onDragLeave : noop}onDragEnter={isDroppable ? this.onDragEnter : noop}onDragOver={isDroppable ? this.onDragOver : noop}>{React.Children.map(this.props.children, child => this.processGridItem(child))}{isDroppable && this.state.droppingDOMNode && this.processGridItem(this.state.droppingDOMNode, true)}{this.placeholder()}</div>);}

GridItem

<GridItem        containerWidth={width}        cols={cols}        margin={margin}        containerPadding={containerPadding || margin}        maxRows={maxRows}        rowHeight={rowHeight}        w={l.w}        h={l.h}        x={l.x}        y={l.y}        i={l.i}        minH={l.minH}        minW={l.minW}        maxH={l.maxH}        maxW={l.maxW}        static={l.static}   cancel={draggableCancel}        handle={draggableHandle}    // 拖拽onDragStop={this.onDragStop}        onDragStart={this.onDragStart}        onDrag={this.onDrag}            isDraggable={draggable}       // 缩放  onResizeStart={this.onResizeStart}        onResize={this.onResize}        onResizeStop={this.onResizeStop}   isResizable={resizable}        resizeHandles={resizeHandlesOptions}        resizeHandle={resizeHandle}  isBounded={bounded}        useCSSTransforms={useCSSTransforms && mounted}        usePercentages={!mounted}        transformScale={transformScale}        droppingPosition={isDroppingItem ? droppingPosition : undefined}        >        {child}      </GridItem>

计算实际位置(top/left/width/height)

function calcGridItemPosition(positionParams,x,y,w,h,state
){const { margin, containerPadding, rowHeight } = positionParams;// 计算列宽 (containerWidth - margin[0] * (cols - 1) - containerPadding[0] * 2) / colsconst colWidth = calcGridColWidth(positionParams);const out = {};// 如果gridItem正在缩放,就采用缩放时state记录的宽高(width,height)。// 通过回调函数获取布局信息if (state && state.resizing) {out.width = Math.round(state.resizing.width);out.height = Math.round(state.resizing.height);}// 反之,基于网格单元计算else {// gridUnits, colOrRowSize, marginPx// Math.round(colOrRowSize * gridUnits + Math.max(0, gridUnits - 1) * marginPx)// margin值计算,会单独留一个margin在下方或者右侧out.width = calcGridItemWHPx(w, colWidth, margin[0]);out.height = calcGridItemWHPx(h, rowHeight, margin[1]);}// 如果gridItem正在拖拽,就采用拖拽时state记录的位置(left,top)// 通过回调函数获取布局信息if (state && state.dragging) {out.top = Math.round(state.dragging.top);out.left = Math.round(state.dragging.left);}// 反之,基于网格单元计算else {out.top = Math.round((rowHeight + margin[1]) * y + containerPadding[1]);out.left = Math.round((colWidth + margin[0]) * x + containerPadding[0]);}return out;
}
// 计算列宽
function calcGridColWidth(positionParams: PositionParams): number {const { margin, containerPadding, containerWidth, cols } = positionParams;return ((containerWidth - margin[0] * (cols - 1) - containerPadding[0] * 2) / cols);
}
// 计算宽高的实际值,最后一个margin会留在当前网格的下面
function calcGridItemWHPx(gridUnits, colOrRowSize, marginPx){// 0 * Infinity === NaN, which causes problems with resize contraintsif (!Number.isFinite(gridUnits)) return gridUnits;return Math.round(colOrRowSize * gridUnits + Math.max(0, gridUnits - 1) * marginPx);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CQGZ37uY-1693221682630)(https://tech-proxy.bytedance.net/tos/images/1693221360608_b333c2cc2256a2207154e26e5bbea246.png)]

拖拽

布局拖拽处理

onDrag

onDrag: (i: string, x: number, y: number, GridDragEvent) => void = (i,x,y,{ e, node }) => {const { oldDragItem } = this.state;let { layout } = this.state;const { cols, allowOverlap, preventCollision } = this.props;const l = getLayoutItem(layout, i);if (!l) return;// Create placeholder (display only)const placeholder = {w: l.w,h: l.h,x: l.x,y: l.y,placeholder: true,i: i};const isUserAction = true;// 根据实时拖拽 生成布局layout = moveElement(layout,l,x,y,isUserAction,preventCollision,compactType(this.props),cols,allowOverlap);this.props.onDrag(layout, oldDragItem, l, placeholder, e, node);this.setState({layout: allowOverlap? layout: compact(layout, compactType(this.props), cols),activeDrag: placeholder});};function moveElement(layout: Layout,l: LayoutItem,x: ?number,y: ?number,isUserAction: ?boolean,preventCollision: ?boolean,compactType: CompactType,cols: number,allowOverlap: ?boolean
): Layout {if (l.static && l.isDraggable !== true) return layout;if (l.y === y && l.x === x) return layout;log(`Moving element ${l.i} to [${String(x)},${String(y)}] from [${l.x},${l.y}]`);const oldX = l.x;const oldY = l.y;//只 修改 xy 更快速if (typeof x === "number") l.x = x;if (typeof y === "number") l.y = y;l.moved = true;// 排序,以便在发生碰撞的时候找最近碰撞的元素let sorted = sortLayoutItems(layout, compactType);const movingUp =compactType === "vertical" && typeof y === "number"? oldY >= y: compactType === "horizontal" && typeof x === "number"? oldX >= x: false;if (movingUp) sorted = sorted.reverse();const collisions = getAllCollisions(sorted, l);const hasCollisions = collisions.length > 0;if (hasCollisions && allowOverlap) {// 允许覆盖时 不处理return cloneLayout(layout);} else if (hasCollisions && preventCollision) {// 不允许碰撞的时候,撤销本次拖拽log(`Collision prevented on ${l.i}, reverting.`);l.x = oldX;l.y = oldY;l.moved = false;return layout;}//处理碰撞for (let i = 0, len = collisions.length; i < len; i++) {const collision = collisions[i];log(`Resolving collision between ${l.i} at [${l.x},${l.y}] and ${collision.i} at [${collision.x},${collision.y}]`);if (collision.moved) continue;if (collision.static) {// 与静态元素碰撞的时候,只能处理当前移动的元素layout = moveElementAwayFromCollision(layout,collision,l,isUserAction,compactType,cols);} else {layout = moveElementAwayFromCollision(layout,l,collision,isUserAction,compactType,cols);}}return layout;
}function moveElementAwayFromCollision(layout: Layout,collidesWith: LayoutItem,itemToMove: LayoutItem,isUserAction: ?boolean,compactType: CompactType,cols: number
): Layout {const compactH = compactType === "horizontal";const compactV = compactType !== "horizontal";const preventCollision = collidesWith.static;if (isUserAction) {isUserAction = false;// 构建元素 出现在碰撞元素的左上方const fakeItem: LayoutItem = {x: compactH ? Math.max(collidesWith.x - itemToMove.w, 0) : itemToMove.x,y: compactV ? Math.max(collidesWith.y - itemToMove.h, 0) : itemToMove.y,w: itemToMove.w,h: itemToMove.h,i: "-1"};if (!getFirstCollision(layout, fakeItem)) {log(`Doing reverse collision on ${itemToMove.i} up to [${fakeItem.x},${fakeItem.y}].`);return moveElement(layout,itemToMove,compactH ? fakeItem.x : undefined,compactV ? fakeItem.y : undefined,isUserAction,preventCollision,compactType,cols);}}return moveElement(layout,itemToMove,compactH ? itemToMove.x + 1 : undefined,compactV ? itemToMove.y + 1 : undefined,isUserAction,preventCollision,compactType,cols);
}

元素拖拽处理

使用mixinDraggable函数,依赖react-draggable中的DraggableCore,为模块提供拖拽能力支持

onDrag

   onDrag: (Event, ReactDraggableCallbackData) => void = (e,{ node, deltaX, deltaY }) => {const { onDrag } = this.props;if (!onDrag) return;if (!this.state.dragging) {throw new Error("onDrag called before onDragStart.");}let top = this.state.dragging.top + deltaY;let left = this.state.dragging.left + deltaX;const { isBounded, i, w, h, containerWidth } = this.props;const positionParams = this.getPositionParams();// 不得超过边界if (isBounded) {const { offsetParent } = node;if (offsetParent) {const { margin, rowHeight } = this.props;const bottomBoundary =offsetParent.clientHeight - calcGridItemWHPx(h, rowHeight, margin[1]);top = clamp(top, 0, bottomBoundary);const colWidth = calcGridColWidth(positionParams);const rightBoundary =containerWidth - calcGridItemWHPx(w, colWidth, margin[0]);left = clamp(left, 0, rightBoundary);}}const newPosition: PartialPosition = { top, left };this.setState({ dragging: newPosition });// Call callback with this dataconst { x, y } = calcXY(positionParams, top, left, w, h);return onDrag.call(this, i, x, y, {e,node,newPosition});};

缩放

布局缩放处理

onResize

onResize: (i: string, w: number, h: number, GridResizeEvent) => void = (i,w,h,{ e, node }) => {const { layout, oldResizeItem } = this.state;const { cols, preventCollision, allowOverlap } = this.props;// 把缩放之后的新元素set到layout中,cb单独处理不可碰撞时的场景const [newLayout, l] = withLayoutItem(layout, i, l => {let hasCollisions;if (preventCollision && !allowOverlap) {const collisions = getAllCollisions(layout, { ...l, w, h }).filter(layoutItem => layoutItem.i !== l.i);hasCollisions = collisions.length > 0;if (hasCollisions) {// 只能向右下角伸缩,因此取x,y 的最小值,保证拖拽元素在碰撞let leastX = Infinity,leastY = Infinity;collisions.forEach(layoutItem => {if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);});if (Number.isFinite(leastX)) l.w = leastX - l.x;if (Number.isFinite(leastY)) l.h = leastY - l.y;}}if (!hasCollisions) {// Set new width and height.l.w = w;l.h = h;}return l;});// Shouldn't ever happen, but typechecking makes it necessaryif (!l) return;// Create placeholder element (display only)const placeholder = {w: l.w,h: l.h,x: l.x,y: l.y,static: true,i: i};this.props.onResize(newLayout, oldResizeItem, l, placeholder, e, node);// Re-compact the newLayout and set the drag placeholder.this.setState({layout: allowOverlap? newLayout: compact(newLayout, compactType(this.props), cols),activeDrag: placeholder});};

元素缩放处理

使用mixinResizable函数,依赖react-resizable中的Resizable,为模块提供缩放能力支持

onResizeHandler

 onResizeHandler(e: Event,{ node, size }: { node: HTMLElement, size: Position },handlerName: string // onResizeStart,  onResize, onResizeStop): void {const handler = this.props[handlerName];if (!handler) return;const { cols, x, y, i, maxH, minH } = this.props;let { minW, maxW } = this.props;// 获取新的宽高let { w, h } = calcWH(this.getPositionParams(), // margin, maxRows, cols, rowHeightsize.width,size.height,x,y);//  保证宽度至少为1minW = Math.max(minW, 1);//  最大宽度不应超过当前行剩余的宽度,缩放 可以挤掉其右侧的模块,但是无法对左侧的模块进行改变maxW = Math.min(maxW, cols - x);// 获取在上下边界内的值w = clamp(w, minW, maxW);h = clamp(h, minH, maxH);this.setState({ resizing: handlerName === "onResizeStop" ? null : size });handler.call(this, i, w, h, { e, node, size });}// px 转 格数
function calcWH(positionParams: PositionParams,width: number,height: number,x: number,y: number
): { w: number, h: number } {const { margin, maxRows, cols, rowHeight } = positionParams;const colWidth = calcGridColWidth(positionParams);let w = Math.round((width + margin[0]) / (colWidth + margin[0]));let h = Math.round((height + margin[1]) / (rowHeight + margin[1]));// 在其边界内取值w = clamp(w, 0, cols - x);h = clamp(h, 0, maxRows - y);return { w, h };
}

ResponsiveGridLayout

响应式布局,主要实现的是针对不同宽度的适配。值得注意的是,如果一定不想给每一个breakpoint配置layout的话,请一定给最大的那个breakpoint配置layout。

配置

breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},// 固定间隔或者响应式间隔 e.g.[10,10]| `{lg: [10, 10], md: [10, 10], ...}.
margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]}// 由断点查找的 layouts 对象 e.g. {lg: Layout, md: Layout, ...}
layouts: {[key: $Keys<breakpoints>]: Layout}// 回调函数
onBreakpointChange: (newBreakpoint: string, newCols: number) => void,
onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,
// 页面宽度变化时的回调,可以根据需要修改 layout 或者处理其他内容
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;

流程

布局生成方法

function findOrGenerateResponsiveLayout(layouts: ResponsiveLayout<Breakpoint>,breakpoints: Breakpoints<Breakpoint>,breakpoint: Breakpoint,lastBreakpoint: Breakpoint,cols: number,compactType: CompactType
): Layout {// 如果有提供对应配置的layout 直接使用if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]);// 另外生成let layout = layouts[lastBreakpoint];const breakpointsSorted = sortBreakpoints(breakpoints);const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint));for (let i = 0, len = breakpointsAbove.length; i < len; i++) {const b = breakpointsAbove[i];if (layouts[b]) {layout = layouts[b];break;}}layout = cloneLayout(layout || []);return compact(correctBounds(layout, { cols: cols }), compactType, cols);
}

总结

**性能好:**200个模块 约268.8ms

独立的是否更新layout的判断机制

用 transform: translateY(-5px) 只是改变了视觉位置,元素本身位置还是在 0px,不改动 css 布局,因为渲染行为大多数情况下在元素本身,所以效率比 top left 要高

功能齐全

https://react-grid-layout.github.io/react-grid-layout/examples/0-showcase.html

https://github.com/react-grid-layout/react-grid-layout/tree/master

相关文章:

react-grid-layout 实现原理介绍

简介 React-grid-layout 是一个基于 React 的网格布局库&#xff0c;它可以帮助我们轻松地在网格中管理和排列组件。它提供了一个直观的 API&#xff0c;可以通过配置网格的大小、列数和组件的位置来实现复杂的布局&#xff0c;还支持对网格中组件的拖拽和重新排列。 实现 诉…...

集合框架-(Collection/Map)

1.单列集合 1.1基础概要 集合中存储的是对象的地址信息&#xff0c;想要输出对象的信息&#xff0c;需要在具体的类中重写toString&#xff08;&#xff09;方法 Collection代表单列集合&#xff0c;每个元素数据只包含一个值 List集合&#xff1a;添加的元素可以是有序、可…...

什么是单文件组件?

单文件组件形式 非单文件组件 可以理解为是通过 html 文件来使用 Vue。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><…...

国际站阿里云服务器多久会重启一次系统??

阿里云服务器是一种高性能、高可靠的云计算服务&#xff0c;但在长时间运行过程中&#xff0c;系统可能会出现一些问题&#xff0c;需要重启来恢复正常运行。那么&#xff0c;阿里云服务器多久重启一次系统呢&#xff1f;本文将对这个问题进行解答。 阿里云服务器重启频率 阿里…...

低成本32位单片机电动工具无感方波控制方案

RAMSUN介绍基于灵动32位微处理器MM32SPIN0230的BLDC电动工具无感方波控制方案&#xff0c;包括MM32SPIN0230芯片资源。 以下是电动工具无感方波控制方案的简述&#xff1a; MM32SPIN0230电动工具专用板 芯片介绍 MM32SPIN0230系列是灵动微MindSPIN旗下高性能的单电机控制产品…...

安防视频监控/视频集中存储/云存储平台EasyCVR平台无法播放HLS协议该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…...

MySQL如何查找某个字段值相同的数据

当我们想要查找MySQL中某个字段值相同的数据&#xff0c;但我们又不知道这个数据的值是什么的时候该如何操作呢&#xff1f; 在我的数据表中有单词表以及对应的详细信息表&#xff0c;如果两张表是以单词作为逻辑上的外键时&#xff0c;查询单词详细信息操作就可以根据word值进…...

8.react18并发模式与startTransition(搜索高亮思路)

React 18 之前,渲染是一个单一的,不间断的,同步的事务,一旦渲染开始,就不能被中断 React 18引入并发模式,它允许你将标记更新作为一个transitions,这会告诉React他们可以被中断执行.这样可以将紧急任务先更新,不紧急任务后更新. 将任务给紧急任务先执行, 优先级低的任务后执行…...

前端Vue自定义得分构成水平柱形图组件 可用于系统专业门类得分评估分析

引入Vue自定义得分构成水平柱形图组件&#xff1a;cc-horBarChart 随着技术的发展&#xff0c;传统的开发方式使得系统的复杂度越来越高&#xff0c;一个小小的改动或小功能的增加可能会导致整体逻辑的修改&#xff0c;造成牵一发而动全身的情况。为了解决这个问题&#xff0c…...

Linux获取纳秒级别时间

在 Linux 系统中可以使用 gettimeofday 函数来获取时间&#xff0c;这个函数能够以毫秒的精度来获取时间 struct timeval tv;gettimeofday(&tv, NULL);time_t cur_time tv.tv_sec;long cur_time_ms tv.tv_usec/1000;printf(“cur_time %d \n”, cur_time);printf(“cur…...

CSS中你不得不知道的盒子模型

目录 1、CSS的盒子模型1.1 css盒子模型有哪些&#xff1a;1.2 css盒子模型的区别1.3 通过css如何转换css盒子模型 1、CSS的盒子模型 1.1 css盒子模型有哪些&#xff1a; 标准盒子模型、怪异盒子模型&#xff08;IE盒子模型&#xff09; 1.2 css盒子模型的区别 标准盒子模型&a…...

知识储备--基础算法篇-数组

1.学习 2.数组 2.1第53题-最大子数组和 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 心得&#xff1a;一直在纠结这个连续的事情&…...

zookeeper 理论合集

目录 系统背景 集群结构 多个节点之间的角色 节点的状态 为什么引入 Observer 存储结构 ZNode 节点结构 ZNode 创建类型 内存数据存储 数据持久化 zookeeper 的容量大小 数据同步 消息广播 崩溃恢复 如何保证顺序一致性 核心流程 Leader 选举流程 脑裂问题 …...

【pyinstaller 怎么打包python,打包后程序闪退 不打日志 找不到自建模块等问题的踩坑解决】

程序打包踩坑解决的所有问题 问题1 多个目录怎么打包 不管你包含多个层目录&#xff0c;引用多么复杂&#xff0c;只需要打包主程序所在文件即可&#xff0c;pyinstaller会自动寻找依赖包&#xff0c;如果报错自建模块找不到&#xff0c;参照问题3 pyinstaller main.py问题2…...

【Docker】网络

文章目录 Docker 网络基础Docker网络管理Docker网络架构CNMLibnetwork驱动 常见的网络类型 Docker 网络管理命令docker network createdocker network inspectdocker network connectdocker network disconnectdocker network prunedocker network rmdocker network ls docker …...

Linux :realpath 命令

以后可以直接用于查找相关文件 例: 输入:realpath .repo/manifests/rv1126_rv1109_linux_release.xml 输出:/home/sdk/work/rk/rv1126_rv1109/.repo/manifests/rv1126_rv1109_linux/rv1126_rv1109_linux_v3.0.2_20230406.xml根据输入的文件找到对应复制过来的型号,这个命令不…...

react17:生命周期函数

挂载时更新时 setState触发更新、父组件重新渲染时触发更新forceUpdate触发更新卸载时 react&#xff08;v17.0.2&#xff09;的生命周期图谱如下。 相较于16版本&#xff0c;17版本生命周期函数有如下变化&#xff1a; componentWillMount() componentWillUpdate() compone…...

腾讯内部单边拥塞算法BBR-TCPA一键脚本安装

TCPA简介 腾讯内部使用的TCPA&#xff0c;由腾讯TEG操作系统组研发&#xff0c;基于RHEL7.4源码&#xff0c;定制化的TCPA。团队介绍&#xff1a;腾讯TEG操作系统组, 2010年成立&#xff0c;专业的内核团队,维护研发腾讯内部linux操作系统tlinux, 保证百万级server高效稳定运行…...

【LLM】chatglm-6B模型训练和推理

本篇文章记录下 chatglm-6B 训练和推理过程 环境&#xff1a;Ubuntu 20.04 1.13.0cu116 chatglm-6B 源代码仓库&#xff1a;链接 chatglm-6B 模型权重&#xff1a;链接 源代码及模型 clone 到本地 这里使用的是 THUDM 在 hugging face 开源的模型。 因为模型比较大&#xff…...

性能可靠it监控系统,性能监控软件的获得来源有哪些

性能可靠的IT监控系统是企业IT运维的重要保障之一。以下是一个性能可靠的IT监控系统应该具备的特点&#xff1a; 高可用性 高可用性是IT监控系统的一个重要特点&#xff0c;它可以保证系统在24小时不间断监控的同时&#xff0c;保证系统服务的可用性和稳定性。为了实现高可用性…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...