实例_尺度标定画布
<h3>构造函数</h3>
<pre><code class="language-javascript">constructor(vm) {
this.vm = vm; // 绑定页面实例
// 定义class前缀
this.pifix = 'RF-SCALECALIBRATION-';
// 默认绘制的是线段
this.type = 'line';
}</code></pre>
<h3>获取基础缩放比例</h3>
<pre><code class="language-javascript">/**
* 获取基础缩放比例
* @param {*} svg
*/
getScale(svg) {
let { width, height } = this.vm.$refs.canvasWrap.getBoundingClientRect();
// 如果初始化的时候 拿不到宽高
if (width === 0 || height === 0) {
width = this.vm.canvasWidth - 2;
height = this.vm.canvasHeight - 76 - 52 - 2;
}
const minW = Math.min(width, height);
// const svgWidth = svg.attr('wrapWidth');
return this.vm.imageWidth / minW;
}</code></pre>
<h3>创建箭头marker</h3>
<pre><code class="language-javascript">/**
* 创建箭头marker
* @param {*} svg
*/
createMarker(svg) {
svg.append('defs')
.html(`
&lt;marker id=&quot;SCALECALIBRATIO-marker-end&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;10&quot; refY=&quot;5&quot;
markerWidth=&quot;${this.getScale(svg) * 1.5}&quot; markerHeight=&quot;${this.getScale(svg) * 1.5}&quot; orient=&quot;auto&quot;&gt;
&lt;path d=&quot;M 0 0 L 10 5 L 0 10&quot; stroke-width=&quot;1&quot; stroke=&quot;#FF9D28&quot; fill=&quot;none&quot; /&gt;
&lt;/marker&gt;
&lt;marker id=&quot;SCALECALIBRATIO-marker-start&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;0&quot; refY=&quot;5&quot;
markerWidth=&quot;${this.getScale(svg) * 1.5}&quot; markerHeight=&quot;${this.getScale(svg) * 1.5}&quot; orient=&quot;auto&quot;&gt;
&lt;path d=&quot;M 10 0 L 0 5 L 10 10&quot; stroke-width=&quot;1&quot; stroke=&quot;#FF9D28&quot; fill=&quot;none&quot; /&gt;
&lt;/marker&gt;
`);
}</code></pre>
<p>指定箭头marker的起始和结尾</p>
<h3>绘制线段</h3>
<pre><code class="language-javascript">/**
* 绘制线
* @param {*} g
* @param {*} x1
* @param {*} y1
* @param {*} x2
* @param {*} y2
* @param {*} cls
* @param {*} width
* @param {*} marker
*/
drawLine(g, x1, y1, x2, y2, cls, width, marker, svg) {
return g.append('line')
.attr('class', cls)
.attr('x1', x1)
.attr('y1', y1)
.attr('x2', x2)
.attr('y2', y2)
.attr('marker-start', marker ? 'url(#SCALECALIBRATIO-marker-start)' : '')
.attr('marker-end', marker ? 'url(#SCALECALIBRATIO-marker-end)' : '')
.attr('stroke-width', this.getScale(svg) * width)
.style('fill', 'none')
.style('stroke', '#FF9D28')
.style('shape-rendering', 'crispEdges'); // 抗锯齿
}</code></pre>
<h3>绘制圆圈</h3>
<pre><code class="language-javascript"> /**
* 绘制圆圈
* @param {*} g
* @param {*} x
* @param {*} y
* @param {*} r
* @param {*} cls
* @param {*} svg
*/
drawCircle(g, x, y, r, cls, svg) {
return g.append('circle')
.attr('class', cls)
.attr('cx', x)
.attr('cy', y)
.attr('r', this.getScale(svg) * r)
.attr('fill', '#FF9D28')
.style('cursor', 'pointer');
}</code></pre>
<h3>绘制路径</h3>
<pre><code class="language-javascript"> /**
* 绘制路径
* @param {*} g
* @param {*} cls
* @param {*} width
*/
drawPath(g, cls, width, svg) {
return g.append('path')
.attr('class', cls)
.style('stroke-width', this.getScale(svg) * width)
.style('stroke', '#FF9D28')
.style('stroke-linejoin', 'round')
.style('fill', 'none');
}</code></pre>
<h3>事件 拖拽Start</h3>
<pre><code class="language-javascript">/**
* 拖拽开始
* @param {*} e
* @param {*} g
*/
dragStart(e, g, svg) {
const { x, y } = e;
if (!svg.select('#SCALECALIBRATIO-marker').size()) {
this.createMarker(svg);
}
this.start = {
x: x,
y: y,
g: null
};
// 每次重新绘制前移除之前的线
g.selectAll('.RF-canvas-g').remove();
const curg = g.append('g')
.attr('class', 'RF-canvas-g');
if (this.type === 'line') { // 线段绘制
this.drawCircle(curg, x, y, 5, `${this.pifix}start`, svg);
// 添加path
const path = this.drawPath(curg, `${this.pifix}line`, 2, svg);
this.start.g = curg;
this.start.path = path;
} else if (this.type === 'verticalRuler') { // 垂直尺
// &lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;200&quot; y2=&quot;200&quot; style=&quot;stroke:rgb(255,0,0);stroke-width:2&quot;/&gt;
const x1 = x - this.getScale(svg) * 40;
const x2 = x + this.getScale(svg) * 40;
// 绘制开始线
this.drawLine(curg, x1, y, x2, y, `${this.pifix}line-start`, 1, false, svg);
// 绘制中间线
this.drawLine(curg, x, y, x, y, `${this.pifix}line-middle`, 1, true, svg);
this.start.g = curg;
} else if (this.type === 'horizontalRuler') { // 水平尺
const y1 = y - this.getScale(svg) * 40;
const y2 = y + this.getScale(svg) * 40;
// 绘制开始线
this.drawLine(curg, x, y1, x, y2, `${this.pifix}line-start`, 1, false, svg);
// 绘制中间线
this.drawLine(curg, x, y, x, y, `${this.pifix}line-middle`, 1, true, svg);
this.start.g = curg;
}
}</code></pre>
<h3>事件拖拽move</h3>
<pre><code class="language-javascript"> /**
* 拖拽move
* @param {*} e
* @param {*} g
*/
drag(e, g, svg) {
const { x, y } = e;
if (this.start) {
// 线段绘制
if (this.type === 'line') {
this.start.path.attr('d', `M${this.start.x},${this.start.y}L${x},${y}`);
} else if (this.type === 'verticalRuler') { // 垂直尺
this.start.g.select(`.${this.pifix}line-middle`)
.attr('x2', this.start.x)
.attr('y2', y);
} else if (this.type === 'horizontalRuler') { // 水平尺
this.start.g.select(`.${this.pifix}line-middle`)
.attr('x2', x)
.attr('y2', this.start.y);
}
}
}</code></pre>
<h3>事件拖拽end</h3>
<pre><code class="language-javascript">/**
* 拖拽结束
* @param {*} e
* @param {*} g
*/
dragEnd(e, g, svg) {
const { x, y } = e;
if (this.start) {
// 线段绘制
if (this.type === 'line') {
this.start.path.attr('d', `M${this.start.x},${this.start.y}L${x},${y}Z`);
this.drawCircle(this.start.g, x, y, 5, `${this.pifix}end`, svg);
// 触发回调函数 返回坐标
this.vm.operatorCb &amp;&amp; this.vm.operatorCb([
[this.start.x, this.vm.imageHeight - this.start.y],
[x, this.vm.imageHeight - y]
], this.vm.getCameraInfo(svg));
// 小圆点可以手动调整位置
this.dragCicle(this.start.g, this.start.path, svg);
} else if (this.type === 'verticalRuler') { // 垂直尺
this.start.g.select(`.${this.pifix}line-middle`)
.attr('x2', this.start.x)
.attr('y2', y);
this.drawLine(this.start.g, this.start.x - this.getScale(svg) * 40, y, this.start.x + this.getScale(svg) * 40, y, `${this.pifix}line-end`, 1, false, svg);
// 触发回调函数 返回坐标
this.vm.operatorCb &amp;&amp; this.vm.operatorCb([
[this.start.x, this.vm.imageHeight - this.start.y],
[this.start.x, this.vm.imageHeight - y]
], this.vm.getCameraInfo(svg));
} else if (this.type === 'horizontalRuler') { // 水平尺
this.start.g.select(`.${this.pifix}line-middle`)
.attr('x2', x)
.attr('y2', this.start.y);
this.drawLine(this.start.g, x, this.start.y - this.getScale(svg) * 40, x, this.start.y + this.getScale(svg) * 40, `${this.pifix}line-end`, 1, false, svg);
// 触发回调函数 返回坐标
this.vm.operatorCb &amp;&amp; this.vm.operatorCb([
[this.start.x, this.vm.imageHeight - this.start.y],
[x, this.vm.imageHeight - this.start.y]
], this.vm.getCameraInfo(svg));
}
}
}</code></pre>
<h3>原点拖动</h3>
<pre><code class="language-javascript">/**
* 原点拖动功能
* @param {*} g
*/
dragCicle(g, path, svg) {
const _ = this;
g.selectAll('circle')
.call(d3.drag()
.on('start', () =&gt; {
this.startCircleMoveing = true;
})
.on('end', function (e) {
_.moveCircle(d3.select(this), d3.event, path, true, svg);
this.startCircleMoveing = false;
})
.on('drag', function (e) {
_.moveCircle(d3.select(this), d3.event, path, svg);
}));
}</code></pre>
<h3>圆点移动</h3>
<pre><code class="language-javascript">/**
* 圆点移动
* @param {*} circle
* @param {*} event
*/
moveCircle(circle, event, path, isEnd, svg) {
if (this.startCircleMoveing) {
const { dx, dy } = event;
const isStart = circle.attr('class') === `${this.pifix}start`;
if (isStart) {
const newx = Number(circle.attr('cx')) + dx;
const newy = Number(circle.attr('cy')) + dy;
circle.attr('cx', newx)
.attr('cy', newy);
// update path
path.attr('d', path.attr('d').replace(/M[\d\.,]+/, `M${newx},${newy}`));
} else {
const newx = Number(circle.attr('cx')) + dx;
const newy = Number(circle.attr('cy')) + dy;
circle.attr('cx', newx)
.attr('cy', newy);
// update path
path.attr('d', path.attr('d').replace(/L[\d\.,]+/, `L${newx},${newy}`));
}
// M4348.178649902344,764.1790771484375L932.2987060546875,1681.1940307617188Z
const arrs = path.attr('d').match(/[\d\.]+/g);
// 触发回调函数 返回坐标
if (arrs &amp;&amp; arrs.length === 4) {
isEnd &amp;&amp; this.vm.operatorCb &amp;&amp; this.vm.operatorCb([
[arrs[0], this.vm.imageHeight - arrs[1]],
[arrs[2], this.vm.imageHeight - arrs[3]]
], this.vm.getCameraInfo(svg));
}
}
}</code></pre>
<h3>清空画布</h3>
<pre><code class="language-javascript"> /**
* 清空画布
*/
clearCanvas(svg) {
if (arguments.length) {
svg = d3.select(svg);
svg.selectAll('.RF-canvas-g').remove();
this.svg = svg;
} else {
this.svg.selectAll('.RF-canvas-g').remove();
}
}</code></pre>
<h3>画布回显</h3>
<pre><code class="language-javascript">/**
* 回显功能
* @param {*} type
* @param {*} data
* {
// 坐标信息
coordinate: [
[1920, 1080],
[2920, 3000]
],
// 类型
type: 'line'
}
*/
echoCanvas(data, svg) {
svg = d3.select(svg);
if (!svg.select('#SCALECALIBRATIO-marker').size()) {
this.createMarker(svg);
}
const g = svg.select('.handle-g');
// 每次重新绘制前移除之前的线
g.selectAll('.RF-canvas-g').remove();
const curg = g.append('g').attr('class', 'RF-canvas-g');
// 画线
if (data.type === 'line' &amp;&amp; data.coordinate.length === 2) {
// eslint-disable-next-line prefer-const
let [x1, y1] = data.coordinate[0];
// eslint-disable-next-line prefer-const
let [x2, y2] = data.coordinate[1];
y1 = this.vm.imageHeight - y1;
y2 = this.vm.imageHeight - y2;
this.drawCircle(curg, x1, y1, 5, `${this.pifix}start`, svg);
// 添加path
const path = this.drawPath(curg, `${this.pifix}line`, 2, svg);
path.attr('d', `M${x1},${y1}L${x2},${y2}Z`);
this.drawCircle(curg, x2, y2, 5, `${this.pifix}end`, svg);
// 小圆点可以手动调整位置
this.dragCicle(curg, path, svg);
}
// 垂直尺子
if (data.type === 'verticalRuler' &amp;&amp; data.coordinate.length === 2) {
// eslint-disable-next-line prefer-const
let [x1, y1] = data.coordinate[0];
// eslint-disable-next-line prefer-const
let [x2, y2] = data.coordinate[1];
y1 = this.vm.imageHeight - y1;
y2 = this.vm.imageHeight - y2;
const xl = x1 - this.getScale(svg) * 40;
const xr = x1 + this.getScale(svg) * 40;
// 绘制开始线
this.drawLine(curg, xl, y1, xr, y1, `${this.pifix}line-start`, 1, false, svg);
// 绘制中间线
this.drawLine(curg, x1, y1, x2, y2, `${this.pifix}line-middle`, 1, true, svg);
// 绘制结束线
this.drawLine(curg, xl, y2, xr, y2, `${this.pifix}line-end`, 1, false, svg);
}
// 水平尺子
if (data.type === 'horizontalRuler' &amp;&amp; data.coordinate.length === 2) {
// eslint-disable-next-line prefer-const
let [x1, y1] = data.coordinate[0];
// eslint-disable-next-line prefer-const
let [x2, y2] = data.coordinate[1];
y1 = this.vm.imageHeight - y1;
y2 = this.vm.imageHeight - y2;
const yt = y1 - this.getScale(svg) * 40;
const yb = y1 + this.getScale(svg) * 40;
// 绘制开始线
this.drawLine(curg, x1, yt, x1, yb, `${this.pifix}line-start`, 1, false, svg);
// 绘制中间线
this.drawLine(curg, x1, y1, x2, y2, `${this.pifix}line-middle`, 1, true, svg);
// 绘制结束线
this.drawLine(curg, x2, yt, x2, yb, `${this.pifix}line-end`, 1, false, svg);
}
}</code></pre>