前端使用 Konva 实现可视化设计器(22)- 绘制图形(矩形、直线、折线)
本章分享一下如何使用 Konva 绘制基础图形:矩形、直线、折线,希望大家继续关注和支持哈!
请大家动动小手,给我一个免费的 Star 吧~
大家如果发现了 Bug,欢迎来提 Issue 哟~
矩形
先上效果!
实现方式基本和《前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)》是一致的,主要区别矩形的大小和椭圆形的大小设置方式不一样,特别是矩形无需设置 offset。其它就不再赘述了哈。
直线、折线
先上效果!
简单描述一下上面的交互:
首先,绘制一条直线,淡出画一条直线还是比较简单的,根据记录鼠标按下的位置和鼠标释放的位置,就很容易得到 Konva.Line 的 points 应该设定的值了。
然后,沿用绘制 椭圆形、矩形 的思路,它只有特定的 2 个“调整点”,分别代表 起点 和 终点。
// src/Render/graphs/Line.ts
// 略
/**
* 直线、折线
*/
export class Line extends BaseGraph {
// 略
constructor(render: Types.Render, dropPoint: Konva.Vector2d) {
super(render, dropPoint, {
type: Types.GraphType.Line,
// 定义了 2 个 调整点
anchors: [{ adjustType: 'start' }, { adjustType: 'end' }].map((o) => ({
adjustType: o.adjustType // 调整点 类型定义
})),
linkAnchors: [
{ x: 0, y: 0, alias: 'start' },
{ x: 0, y: 0, alias: 'end' }
] as Types.AssetInfoPoint[]
})
// 新建 直线、折线
this.line = new Konva.Line({
name: 'graph',
x: 0,
y: 0,
stroke: 'black',
strokeWidth: 1,
hitStrokeWidth: render.toStageValue(5)
})
// 给予 1 像素,防止导出图片 toDataURL 失败
this.group.size({
width: 1,
height: 1
})
// 加入
this.group.add(this.line)
// 鼠标按下位置 作为起点
this.group.position(this.dropPoint)
}
// 实现:拖动进行时
override drawMove(point: Konva.Vector2d): void {
// 鼠标拖动偏移量
const offsetX = point.x - this.dropPoint.x,
offsetY = point.y - this.dropPoint.y
// 起点、终点
const linkPoints = [
[this.line.x(), this.line.y()],
[this.line.x() + offsetX, this.line.y() + offsetY]
]
// 直线、折线 路径
this.line.points(_.flatten(linkPoints))
// 更新 图形 的 调整点 的 锚点位置
Line.updateAnchorShadows(this.group, this.anchorShadows, this.line)
// 更新 图形 的 连接点 的 锚点位置
Line.updateLinkAnchorShadows(this.group, this.linkAnchorShadows, this.line)
// 重绘
this.render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
}
// 实现:拖动结束
override drawEnd(): void {
if (this.line.width() <= 1 && this.line.height() <= 1) {
// 加入只点击,无拖动
// 默认大小
const width = Line.size,
height = width
// 起点、终点
const linkPoints = [
[this.line.x(), this.line.y()],
[this.line.x() + width, this.line.y() + height]
]
// 直线、折线 位置大小
this.line.points(_.flatten(linkPoints))
}
// 更新 调整点(拐点)
Line.updateAnchor(this.render, this.group)
// 更新 图形 的 调整点 的 锚点位置
Line.updateAnchorShadows(this.group, this.anchorShadows, this.line)
// 更新 图形 的 连接点 的 锚点位置
Line.updateLinkAnchorShadows(this.group, this.linkAnchorShadows, this.line)
// 对齐线清除
this.render.attractTool.alignLinesClear()
// 更新历史
this.render.updateHistory()
// 重绘
this.render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
}
// 略
}
调整点,可以改变 直线、折线 的 起点、终点。
// 略
/**
* 直线、折线
*/
export class Line extends BaseGraph {
// 实现:更新 图形 的 调整点 的 锚点位置
static override updateAnchorShadows(
graph: Konva.Group,
anchorShadows: Konva.Circle[],
shape?: Konva.Line
): void {
if (shape) {
const points = shape.points()
//
for (const shadow of anchorShadows) {
switch (shadow.attrs.adjustType) {
case 'start':
shadow.position({
x: points[0],
y: points[1]
})
break
case 'end':
shadow.position({
x: points[points.length - 2],
y: points[points.length - 1]
})
break
}
}
}
}
// 略
// 实现:生成 调整点
static override createAnchorShapes(
render: Types.Render,
graph: Konva.Group,
anchorAndShadows: {
anchor: Types.GraphAnchor
anchorShadow: Konva.Circle
shape?: Konva.Shape
}[],
adjustAnchor?: Types.GraphAnchor
): {
anchorAndShadows: {
anchor: Types.GraphAnchor
anchorShadow: Konva.Circle
shape?: Konva.Shape | undefined
}[]
} {
// stage 状态
const stageState = render.getStageState()
const graphShape = graph.findOne('.graph') as Konva.Line
if (graphShape) {
const points = graphShape.points()
for (const anchorAndShadow of anchorAndShadows) {
let rotate = 0
const { anchor, anchorShadow } = anchorAndShadow
const x = render.toStageValue(anchorShadow.getAbsolutePosition().x - stageState.x),
y = render.toStageValue(anchorShadow.getAbsolutePosition().y - stageState.y)
if (anchor.adjustType === 'manual') {
// 略
} else {
if (anchor.adjustType === 'start') {
rotate = Line.calculateAngle(points[2] - points[0], points[3] - points[1])
} else if (anchor.adjustType === 'end') {
rotate = Line.calculateAngle(
points[points.length - 2] - points[points.length - 4],
points[points.length - 1] - points[points.length - 3]
)
}
const cos = Math.cos((rotate * Math.PI) / 180)
const sin = Math.sin((rotate * Math.PI) / 180)
const offset = render.toStageValue(render.pointSize + 5)
const offsetX = offset * sin
const offsetY = offset * cos
const anchorShape = new Konva.Circle({
name: 'anchor',
anchor: anchor,
//
fill:
adjustAnchor?.adjustType === anchor.adjustType && adjustAnchor?.groupId === graph.id()
? 'rgba(0,0,255,0.8)'
: 'rgba(0,0,255,0.2)',
radius: render.toStageValue(3),
strokeWidth: 0,
// 位置
x: x,
y: y,
offsetX:
anchor.adjustType === 'start' ? offsetX : anchor.adjustType === 'end' ? -offsetX : 0,
offsetY:
anchor.adjustType === 'start' ? offsetY : anchor.adjustType === 'end' ? -offsetY : 0,
// 旋转角度
rotation: graph.getAbsoluteRotation()
})
anchorShape.on('mouseenter', () => {
anchorShape.fill('rgba(0,0,255,0.8)')
document.body.style.cursor = 'move'
})
anchorShape.on('mouseleave', () => {
anchorShape.fill(
anchorShape.attrs.adjusting ? 'rgba(0,0,255,0.8)' : 'rgba(0,0,255,0.2)'
)
document.body.style.cursor = anchorShape.attrs.adjusting ? 'move' : 'default'
})
anchorAndShadow.shape = anchorShape
}
}
}
return { anchorAndShadows }
}
// 略
// 实现:调整 图形
static override adjust(
render: Types.Render,
graph: Konva.Group,
graphSnap: Konva.Group,
adjustShape: Konva.Shape,
anchorAndShadows: {
anchor: Types.GraphAnchor
anchorShadow: Konva.Circle
shape?: Konva.Shape | undefined
}[],
startPoint: Konva.Vector2d,
endPoint: Konva.Vector2d
) {
// 目标 直线、折线
const line = graph.findOne('.graph') as Konva.Line
// 镜像
const lineSnap = graphSnap.findOne('.graph') as Konva.Line
// 调整点 锚点
const anchors = (graph.find('.anchor') ?? []) as Konva.Circle[]
// 镜像
const anchorsSnap = (graphSnap.find('.anchor') ?? []) as Konva.Circle[]
// 连接点 锚点
const linkAnchors = (graph.find('.link-anchor') ?? []) as Konva.Circle[]
if (line && lineSnap) {
// stage 状态
const stageState = render.getStageState()
{
const [graphRotation, adjustType, ex, ey] = [
Math.round(graph.rotation()),
adjustShape.attrs.anchor?.adjustType,
endPoint.x,
endPoint.y
]
const { x: cx, y: cy, width: cw, height: ch } = graphSnap.getClientRect()
const { x, y } = graph.position()
const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]
const { x: sx, y: sy } = Line.rotatePoint(ex, ey, centerX, centerY, -graphRotation)
const { x: rx, y: ry } = Line.rotatePoint(x, y, centerX, centerY, -graphRotation)
const points = line.points()
const manualPoints = (line.attrs.manualPoints ?? []) as Types.LineManualPoint[]
if (adjustType === 'manual') {
// 略
} else {
const anchor = anchors.find((o) => o.attrs.adjustType === adjustType)
const anchorShadow = anchorsSnap.find((o) => o.attrs.adjustType === adjustType)
if (anchor && anchorShadow) {
{
const linkPoints = [
[points[0], points[1]],
...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
[points[points.length - 2], points[points.length - 1]]
]
switch (adjustType) {
case 'start':
{
linkPoints[0] = [sx - rx, sy - ry]
line.points(_.flatten(linkPoints))
}
break
case 'end':
{
linkPoints[linkPoints.length - 1] = [sx - rx, sy - ry]
line.points(_.flatten(linkPoints))
}
break
}
}
}
}
}
// 更新 调整点(拐点)
Line.updateAnchor(render, graph)
// 更新 调整点 的 锚点 位置
Line.updateAnchorShadows(graph, anchors, line)
// 更新 图形 的 连接点 的 锚点位置
Line.updateLinkAnchorShadows(graph, linkAnchors, line)
// 更新 调整点 位置
for (const anchor of anchors) {
for (const { shape } of anchorAndShadows) {
if (shape) {
if (shape.attrs.anchor?.adjustType === anchor.attrs.adjustType) {
const anchorShadow = graph
.find(`.anchor`)
.find((o) => o.attrs.adjustType === anchor.attrs.adjustType)
if (anchorShadow) {
shape.position({
x: render.toStageValue(anchorShadow.getAbsolutePosition().x - stageState.x),
y: render.toStageValue(anchorShadow.getAbsolutePosition().y - stageState.y)
})
shape.rotation(graph.getAbsoluteRotation())
}
}
}
}
}
// 重绘
render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
}
}
// 略
}
折线
相比绘制 椭圆形、矩形 比较不一样的地方在于,椭圆形、矩形 的“调整点”是固定的,而绘制 折线 不一样,没调整一个新的拐点,就会新增 2 个新调整点,整体交互与 手动连接线 类似。
// src/Render/draws/GraphDraw.ts
// 略
export interface GraphDrawState {
// 略
/**
* 调整中 调整点
*/
adjustAnchor?: Types.GraphAnchor
/**
* 鼠标按下 调整点 位置
*/
startPointCurrent: Konva.Vector2d
/**
* 图形 group
*/
graphCurrent?: Konva.Group
/**
* 图形 group 镜像,用于计算位置、大小的偏移
*/
graphCurrentSnap?: Konva.Group
}
// 略
export class GraphDraw extends Types.BaseDraw implements Types.Draw {
// 略
state: GraphDrawState = {
adjusting: false,
adjustGroupId: '',
startPointCurrent: { x: 0, y: 0 }
}
// 略
override draw() {
this.clear()
// 所有图形
const graphs = this.render.layer
.find('.asset')
.filter((o) => o.attrs.assetType === Types.AssetType.Graph) as Konva.Group[]
for (const graph of graphs) {
// 非选中状态才显示 调整点
if (!graph.attrs.selected) {
// 略
for (const anchorAndShadow of anchorAndShadows) {
const { shape } = anchorAndShadow
if (shape) {
// 鼠标按下
shape.on('mousedown', () => {
const pos = this.getStagePoint()
if (pos) {
this.state.adjusting = true
this.state.adjustAnchor = shape.attrs.anchor
this.state.adjustGroupId = graph.id()
this.state.startPointCurrent = pos
this.state.graphCurrent = graph
this.state.graphCurrentSnap = graph.clone()
shape.setAttr('adjusting', true)
if (this.state.adjustAnchor) {
switch (shape.attrs.anchor?.type) {
case Types.GraphType.Line:
// 使用 直线、折线 静态处理方法
Graphs.Line.adjustStart(this.render, graph, this.state.adjustAnchor, pos)
break
}
}
}
})
// 略
// 调整结束
this.render.stage.on('mouseup', () => {
// 略
this.state.adjusting = false
this.state.adjustAnchor = undefined
this.state.adjustGroupId = ''
// 恢复显示所有 调整点
for (const { shape } of anchorAndShadows) {
if (shape) {
shape.opacity(1)
shape.setAttr('adjusting', false)
if (shape.attrs.anchor?.type === Types.GraphType.Line) {
if (shape.attrs.anchor.adjusted) {
shape.fill('rgba(0,0,0,0.4)')
} else {
shape.fill('rgba(0,0,255,0.2)')
}
} else {
shape.stroke('rgba(0,0,255,0.2)')
}
}
// 略
}
// 略
})
// 略
}
}
}
}
}
}
上面除了需要更多的状态记录 调整 信息,还需要定义 Line 特有的 adjustStart 方法:
// src/Render/graphs/Line.ts
// 略
/**
* 直线、折线
*/
export class Line extends BaseGraph {
// 略
/**
* 调整之前
*/
static adjustStart(
render: Types.Render,
graph: Konva.Group,
adjustAnchor: Types.GraphAnchor & { manualIndex?: number; adjusted?: boolean },
endPoint: Konva.Vector2d
) {
const { x: gx, y: gy } = graph.position()
const shape = graph.findOne('.graph') as Konva.Line
if (shape && typeof adjustAnchor.manualIndex === 'number') {
const manualPoints = (shape.attrs.manualPoints ?? []) as Types.LineManualPoint[]
if (adjustAnchor.adjusted) {
//
} else {
manualPoints.push({
x: endPoint.x - gx,
y: endPoint.y - gy,
index: adjustAnchor.manualIndex
})
shape.setAttr('manualPoints', manualPoints)
}
// 更新 调整点(拐点)
Line.updateAnchor(render, graph)
}
}
}
// 略
动态的调整点,会记录在 line 的 attrs 中 manualPoints,每次首次调整一处 拐点,就会新增一个 新 拐点,主要应用在:
// 略
/**
* 直线、折线
*/
export class Line extends BaseGraph {
// 略
// 实现:调整 图形
static override adjust(
render: Types.Render,
graph: Konva.Group,
graphSnap: Konva.Group,
adjustShape: Konva.Shape,
anchorAndShadows: {
anchor: Types.GraphAnchor
anchorShadow: Konva.Circle
shape?: Konva.Shape | undefined
}[],
startPoint: Konva.Vector2d,
endPoint: Konva.Vector2d
) {
// 目标 直线、折线
const line = graph.findOne('.graph') as Konva.Line
// 镜像
const lineSnap = graphSnap.findOne('.graph') as Konva.Line
// 调整点 锚点
const anchors = (graph.find('.anchor') ?? []) as Konva.Circle[]
// 镜像
const anchorsSnap = (graphSnap.find('.anchor') ?? []) as Konva.Circle[]
// 连接点 锚点
const linkAnchors = (graph.find('.link-anchor') ?? []) as Konva.Circle[]
if (line && lineSnap) {
// stage 状态
const stageState = render.getStageState()
{
const [graphRotation, adjustType, ex, ey] = [
Math.round(graph.rotation()),
adjustShape.attrs.anchor?.adjustType,
endPoint.x,
endPoint.y
]
const { x: cx, y: cy, width: cw, height: ch } = graphSnap.getClientRect()
const { x, y } = graph.position()
const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]
const { x: sx, y: sy } = Line.rotatePoint(ex, ey, centerX, centerY, -graphRotation)
const { x: rx, y: ry } = Line.rotatePoint(x, y, centerX, centerY, -graphRotation)
const points = line.points()
const manualPoints = (line.attrs.manualPoints ?? []) as Types.LineManualPoint[]
if (adjustType === 'manual') {
if (adjustShape.attrs.anchor?.manualIndex !== void 0) {
const index = adjustShape.attrs.anchor?.adjusted
? adjustShape.attrs.anchor?.manualIndex
: adjustShape.attrs.anchor?.manualIndex + 1
const manualPointIndex = manualPoints.findIndex((o) => o.index === index)
if (manualPointIndex > -1) {
manualPoints[manualPointIndex].x = sx - rx
manualPoints[manualPointIndex].y = sy - ry
}
const linkPoints = [
[points[0], points[1]],
...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
[points[points.length - 2], points[points.length - 1]]
]
line.setAttr('manualPoints', manualPoints)
line.points(_.flatten(linkPoints))
//
const adjustAnchorShadow = anchors.find(
(o) => o.attrs.adjustType === 'manual' && o.attrs.manualIndex === index
)
if (adjustAnchorShadow) {
adjustAnchorShadow.position({
x: sx - rx,
y: sy - ry
})
}
}
} else {
// 略
}
}
// 略
}
}
// 略
/**
* 更新 调整点(拐点)
* @param render
* @param graph
*/
static updateAnchor(render: Types.Render, graph: Konva.Group) {
const anchors = graph.attrs.anchors ?? []
const anchorShadows = graph.find('.anchor') ?? []
const shape = graph.findOne('.graph') as Konva.Line
if (shape) {
// 已拐
let manualPoints = (shape.attrs.manualPoints ?? []) as Types.LineManualPoint[]
const points = shape.points()
// 调整点 + 拐点
const linkPoints = [
[points[0], points[1]],
...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
[points[points.length - 2], points[points.length - 1]]
]
// 清空 调整点(拐点),保留 start end
anchors.splice(2)
const shadows = anchorShadows.splice(2)
for (const shadow of shadows) {
shadow.remove()
shadow.destroy()
}
manualPoints = []
for (let i = linkPoints.length - 1; i > 0; i--) {
linkPoints.splice(i, 0, [])
}
// 调整点(拐点)
for (let i = 1; i < linkPoints.length - 1; i++) {
const anchor = {
type: graph.attrs.graphType,
adjustType: 'manual',
//
name: 'anchor',
groupId: graph.id(),
//
manualIndex: i,
adjusted: false
}
if (linkPoints[i].length === 0) {
anchor.adjusted = false
// 新增
const prev = linkPoints[i - 1]
const next = linkPoints[i + 1]
const circle = new Konva.Circle({
adjustType: anchor.adjustType,
anchorType: anchor.type,
name: anchor.name,
manualIndex: anchor.manualIndex,
radius: 0,
// radius: render.toStageValue(2),
// fill: 'red',
//
x: (prev[0] + next[0]) / 2,
y: (prev[1] + next[1]) / 2,
anchor
})
graph.add(circle)
} else {
anchor.adjusted = true
// 已拐
const circle = new Konva.Circle({
adjustType: anchor.adjustType,
anchorType: anchor.type,
name: anchor.name,
manualIndex: anchor.manualIndex,
adjusted: true,
radius: 0,
// radius: render.toStageValue(2),
// fill: 'red',
//
x: linkPoints[i][0],
y: linkPoints[i][1],
anchor
})
graph.add(circle)
manualPoints.push({
x: linkPoints[i][0],
y: linkPoints[i][1],
index: anchor.manualIndex
})
}
anchors.push(anchor)
}
shape.setAttr('manualPoints', manualPoints)
graph.setAttr('anchors', anchors)
}
}
// 略
}
上面简单的说,就是处理 manualPoints 的算法,负责控制新增拐点,然后把“点”们插入到 起点、终点 之间,最后处理成 Konva.Line 的 points 的值。
顺带一说。区分 起点、终点 和 拐点 是通过 attrs 中的 adjustType 字段;区分 拐点 是否已经操作过 是通过 attrs 中的 adjusted 字段;拐点是存在明确的顺序的,会记录在 attrs 的 manualIndex 字段中。
个人觉得,目前,绘制图形的 代码结构 和 变量命名 容易产生歧义,后面尽量抽出时间重构一下,大家支持支持 !
Thanks watching~
More Stars please!勾勾手指~
前端使用 Konva 实现可视化设计器(22)- 绘制图形(矩形、直线、折线)的更多相关文章
- 惊闻企业Web应用生成平台 活字格 V4.0 免费了,不单可视化设计器免费,服务器也免费!
官网消息: 针对活字格开发者,新版本完全免费!您可下载活字格 Web 应用生成平台 V4.0 Updated 1,方便的创建各类 Web 应用系统,任意部署,永不过期. 我之前学习过活字格,也曾经向用 ...
- ActiveReports 9 新功能:可视化查询设计器(VQD)介绍
在最新发布的ActiveReports 9报表控件中添加了多项新功能,以帮助你在更短的时间里创建外观绚丽.功能强大的报表系统,本文将重点介绍可视化数据查询设计器,无需手动编写任何SQL语句,主要内容如 ...
- (原创)【B4A】一步一步入门02:可视化界面设计器、控件的使用
一.前言 上篇 (原创)[B4A]一步一步入门01:简介.开发环境搭建.HelloWorld 中我们创建了默认的项目,现在我们来看一下B4A项目的构成,以及如何所见即所得的设计界面,并添加和使用自带的 ...
- Windows Phone 十二、设计器同步
在设计阶段为页面添加数据源 Blend或者VS的可视化设计器会跑我们的代码,然后来显示出来,当我们Build之后,设计器会进入页面的构造函数,调用InitializeComponent();方法来将U ...
- WinForms项目升级.Net Core 3.0之后,没有WinForm设计器?
目录 .NET Conf 2019 Window Forms 设计器 .NET Conf 2019 2019 9.23-9.25召开了 .NET Conf 2019 大会,大会宣布了 .Net Cor ...
- Activiti工作流学习之SpringBoot整合Activiti5.22.0实现在线设计器(二)
一.概述 网上有很多关于Eclipse.IDEA等IDE插件通过拖拽的方式来画工作流程图,个人觉得还是不够好,所以花点时间研究了一下Activiti在线设计器,并与SpringBoot整合. 二.实现 ...
- VS2015 android 设计器不能可视化问题解决。
近期安装了VS2015,体验了一下android 的开发,按模板创建执行了个,试下效果非常不错.也能够可视化设计.但昨天再次打开或创建一个android程序后,设计界面直接不能显示,显示错误:(可能是 ...
- 可视化流程设计——流程设计器演示(基于Silverlight)
上一篇文章<通用流程设计>对鄙人写的通用流程做了一定的介绍,并奉上了相关源码.但一个好的流程设计必少不了流程设计器的支持,本文将针对<通用流程设计>中的流程的设计器做一个简单的 ...
- F2工作流引擎之-纯JS Web在线可拖拽的流程设计器(八)
Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回.传阅.转交,都可以非常方便快捷地实现,管理员 ...
- 纯JS Web在线可拖拽的流程设计器
F2工作流引擎之-纯JS Web在线可拖拽的流程设计器 Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回. ...
随机推荐
- SQLServer 的Distinct
distinct去除重复的数据(distinct是对整个结果集进行数据重复处理,不是针对某一列) -> 检查返回不重复的数据(对于整条记录不重复才会去除,如ID不一样) 用法:select di ...
- 【解决方案】智能UI自动化测试
你的UI自动化追得上业务的变更和UI更迭吗?当今瞬息万变的时代,成千上万的App围绕着现代人生活的点点滴滴.为了满足用户的好的体验和时刻的新鲜感,这些App需要时刻保持变化,也给 UI自动化落地实施带 ...
- 如何更好的使用 Windows
如何更好的使用 Windows Microsoft 辅助功能和工具 键盘快捷方式,常用 ctrl+C 复制 ctrl+V 粘贴 ctrl+X 剪切 ctrl+Z 撤销 ctrl+Y 回退 alt+ta ...
- 手把手教你!STM32单片机入门指南:从初级到中级工程师的学习路线
在当今科技日新月异的时代,嵌入式系统作为智能设备的核心驱动力,正以前所未有的速度渗透到我们生活的方方面面.STM32系列微控制器,以其高性能.低功耗及丰富的外设资源,成了许多开发者踏入嵌入式领域首选 ...
- ansible 一键部署openstack (双节点)
1.三台虚拟机设置 ansible 内存 2GB 处理器 4 硬盘 40GB 光盘iso centos1804 网络适配器 仅主机模式 显示器 自动检测 controller 内存 5.3GB 处理器 ...
- UDP协议测试
UDP协议测试 我们一般想到测试连通性时第一考虑到的就是使用ping命令. 但是我们知道ping命令使用的是icmp协议,属于tcp/ip协议中的一个子协议,所以我们可以用ping或tcping命令来 ...
- 【Java】 WebService 校验机制
测试环境域名 不可见 正式环境域名 不可见 1.2.安全校验凭证 accessId(授权ID) 测试/正式待定 securityKey(加密密钥) 测试/正式待定 1.3.安全校验机制 1.3.1.在 ...
- 【设计模式 Design Pattern】【UML】建模语言
什么是UML图? UML-Unified Modeling Language 统一建模语言,又称标准建模语言. 是用来对软件密集系统进行可视化建模的一种语言. UML的定义包括UML语义和UML表示法 ...
- 【Spring-Security】Re10 Oauth2协议 P1 授权码模式 & 密码模式
一.Oauth2协议: 第三方登录,即忘记本站密码,但是登录界面中提供了一些第三方登录,例如微信.支付宝.QQ.等等,通过第三方授权实现登录 第三方认证技术主要解决的时认证标准,各个平台的登录要遵循统 ...
- 预处理共轭梯度算法(Preconditioned Conjugate Gradients Method)
预处理共轭梯度算法(Preconditioned Conjugate Gradients Method) 给出百度百科上的解释: 预处理共轭梯度法 预处理共轭梯度法是.不必预先估计参数等特点. 共轭梯 ...