/* eslint-disable @typescript-eslint/prefer-for-of */ /** * Polyline 多点线段 * * ```javascript * new G.Shape.Polyline({ * attrs: { * points: [ * {x: 100, y: 100}, * {x: 200, y: 200}, * {x: 300, y: 300}, * {x: 200, y: 0}, 0值 正常渲染 * {x: 200, y: null}, null空值 不渲染 * {x: 200, y: undefied}, 异常值 不渲染 * {x: 300, y: 300}, * ], * smooth: true | false, // 是否转曲线,默认为 false,绘制曲线时使用 * stroke: 'red' // html5 canvas 绘图属性 * lineWidth: 1, // html5 canvas 绘图属性 * } */ import Shape from './shape'; import { PolyLineOptions } from '../models/painter'; import * as Smooth from '../utils/smooth'; // filter the point which x or y is NaN // 【warning】 isNaN: true-string/undefined false-number/null function filterPoints(points) { const filteredPoints = []; for (let i = 0; i < points.length; i++) { const point = points[i]; const hasNaN = isNaN(point.x) || isNaN(point.y); const hasNull = point.x === null || point.y === null; const isValid = !hasNaN && !hasNull; if (isValid) { filteredPoints.push(point); } } return filteredPoints; } // 线段分割 function getValidPoints(rawPoints) { // const segLines: Array<{ x: number; y: number }>[] = []; const validPoints: boolean[] = []; for (let i = 0; i < rawPoints.length; i++) { const point = rawPoints[i]; const hasNaN = isNaN(point.x) || isNaN(point.y); const hasNull = point.x === null || point.y === null; const isValid = !hasNaN && !hasNull; validPoints.push(isValid); } return validPoints; } class PolyLine extends Shape { draw(): void { const { config, ctx } = this; const { rawPoints = [], // 数学坐标系坐标数组 points = [], // web坐标系坐标数组 smooth = false, stroke = '#000', lineWidth = 1, } = config as PolyLineOptions; const validPoints = getValidPoints(rawPoints);// 坐标合法指示数组 const filteredPoints = filterPoints(points); ctx.save(); ctx.beginPath(); // 样式 ctx.strokeStyle = stroke; ctx.lineWidth = lineWidth; /** * 多折线绘制 * 曲线绘制:moveTo起点,basierCurveTo传入三个关键参数(前两个空值点,第三个终点); * 直线绘制:moveTo起点,lineTo终点 *【关键区别】bazierCurveTo后并未移动坐标至终点,还需要手动moveTo一下才行! */ if (filteredPoints.length) { if (smooth) { const constaint = [ [0, 0], [1, 1], ]; const sps = Smooth.smooth(filteredPoints, false, constaint); // 【备注】sps指的是线段,故如果points.length=6,sps.length=5 for (let i = 0, j = sps.length; i < j; i++) { ctx.moveTo(filteredPoints[i].x, filteredPoints[i].y); if (!validPoints[i]) continue; // else if (validPoints[i] && i + 1 === j) continue; else if (validPoints[i] && i + 1 < j && !validPoints[i + 1]) continue; else { const sp = sps[i]; ctx.bezierCurveTo(sp[1], sp[2], sp[3], sp[4], sp[5], sp[6]); } } } else { // 先绘制第一个非空点 let startIndex = 0; while (!validPoints[startIndex]) startIndex = startIndex + 1; ctx.moveTo(filteredPoints[startIndex].x, filteredPoints[startIndex].y); // 从第二个点 -> 第一个点 for (let i = startIndex + 1, l = filteredPoints.length; i < l; i++) { const isPointValid = validPoints[i]; if (isPointValid) { ctx.lineTo(filteredPoints[i].x, filteredPoints[i].y); } else { if (i + 1 >= l) continue; ctx.moveTo(filteredPoints[i + 1].x, filteredPoints[i + 1].y); } } } } ctx.stroke(); ctx.closePath(); ctx.restore(); } } export default PolyLine;