123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- import { ElementEvent, AABB, CustomEvent, CanvasEvent, Shape, isPattern, GradientType, runtime, AbstractRendererPlugin } from '@antv/g-lite';
- import { isNil, isString } from '@antv/util';
- import { mat4, vec3 } from 'gl-matrix';
- function _extends() {
- _extends = Object.assign ? Object.assign.bind() : function (target) {
- for (var i = 1; i < arguments.length; i++) {
- var source = arguments[i];
- for (var key in source) {
- if (Object.prototype.hasOwnProperty.call(source, key)) {
- target[key] = source[key];
- }
- }
- }
- return target;
- };
- return _extends.apply(this, arguments);
- }
- function _inheritsLoose(subClass, superClass) {
- subClass.prototype = Object.create(superClass.prototype);
- subClass.prototype.constructor = subClass;
- _setPrototypeOf(subClass, superClass);
- }
- function _setPrototypeOf(o, p) {
- _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
- o.__proto__ = p;
- return o;
- };
- return _setPrototypeOf(o, p);
- }
- /**
- * support 2 modes in rendering:
- * * immediate
- * * delayed: render at the end of frame with dirty-rectangle
- */
- var CanvasRendererPlugin = /*#__PURE__*/function () {
- function CanvasRendererPlugin(canvasRendererPluginOptions) {
- this.canvasRendererPluginOptions = void 0;
- this.context = void 0;
- this.pathGeneratorFactory = void 0;
- /**
- * RBush used in dirty rectangle rendering
- */
- this.rBush = void 0;
- this.removedRBushNodeAABBs = [];
- this.renderQueue = [];
- /**
- * This stack is only used by clipPath for now.
- */
- this.restoreStack = [];
- this.clearFullScreen = false;
- /**
- * view projection matrix
- */
- this.vpMatrix = mat4.create();
- this.dprMatrix = mat4.create();
- this.tmpMat4 = mat4.create();
- this.vec3a = vec3.create();
- this.vec3b = vec3.create();
- this.vec3c = vec3.create();
- this.vec3d = vec3.create();
- this.canvasRendererPluginOptions = canvasRendererPluginOptions;
- }
- var _proto = CanvasRendererPlugin.prototype;
- _proto.apply = function apply(context, runtime) {
- var _this = this;
- this.context = context;
- var config = context.config,
- camera = context.camera,
- renderingService = context.renderingService,
- renderingContext = context.renderingContext,
- rBushRoot = context.rBushRoot,
- pathGeneratorFactory = context.pathGeneratorFactory;
- this.rBush = rBushRoot;
- this.pathGeneratorFactory = pathGeneratorFactory;
- var contextService = context.contextService;
- var canvas = renderingContext.root.ownerDocument.defaultView;
- var handleUnmounted = function handleUnmounted(e) {
- var object = e.target;
- // remove r-bush node
- // @ts-ignore
- var rBushNode = object.rBushNode;
- if (rBushNode.aabb) {
- // save removed aabbs for dirty-rectangle rendering later
- _this.removedRBushNodeAABBs.push(rBushNode.aabb);
- }
- };
- var handleCulled = function handleCulled(e) {
- var object = e.target;
- // @ts-ignore
- var rBushNode = object.rBushNode;
- if (rBushNode.aabb) {
- // save removed aabbs for dirty-rectangle rendering later
- _this.removedRBushNodeAABBs.push(rBushNode.aabb);
- }
- };
- renderingService.hooks.init.tap(CanvasRendererPlugin.tag, function () {
- canvas.addEventListener(ElementEvent.UNMOUNTED, handleUnmounted);
- canvas.addEventListener(ElementEvent.CULLED, handleCulled);
- // clear fullscreen
- var dpr = contextService.getDPR();
- var width = config.width,
- height = config.height;
- var context = contextService.getContext();
- _this.clearRect(context, 0, 0, width * dpr, height * dpr, config.background);
- });
- renderingService.hooks.destroy.tap(CanvasRendererPlugin.tag, function () {
- canvas.removeEventListener(ElementEvent.UNMOUNTED, handleUnmounted);
- canvas.removeEventListener(ElementEvent.CULLED, handleCulled);
- // this.renderQueue = [];
- // this.removedRBushNodeAABBs = [];
- // this.restoreStack = [];
- });
- renderingService.hooks.beginFrame.tap(CanvasRendererPlugin.tag, function () {
- var context = contextService.getContext();
- var dpr = contextService.getDPR();
- var width = config.width,
- height = config.height;
- var _this$canvasRendererP = _this.canvasRendererPluginOptions,
- dirtyObjectNumThreshold = _this$canvasRendererP.dirtyObjectNumThreshold,
- dirtyObjectRatioThreshold = _this$canvasRendererP.dirtyObjectRatioThreshold;
- // some heuristic conditions such as 80% object changed
- var _renderingService$get = renderingService.getStats(),
- total = _renderingService$get.total,
- rendered = _renderingService$get.rendered;
- var ratio = rendered / total;
- _this.clearFullScreen = renderingService.disableDirtyRectangleRendering() || rendered > dirtyObjectNumThreshold && ratio > dirtyObjectRatioThreshold;
- if (context) {
- context.resetTransform ? context.resetTransform() : context.setTransform(1, 0, 0, 1, 0, 0);
- if (_this.clearFullScreen) {
- _this.clearRect(context, 0, 0, width * dpr, height * dpr, config.background);
- }
- }
- });
- var renderByZIndex = function renderByZIndex(object, context) {
- if (object.isVisible() && !object.isCulled()) {
- _this.renderDisplayObject(object, context, _this.context, _this.restoreStack, runtime);
- // if (object.renderable.) {
- // if we did a full screen rendering last frame
- _this.saveDirtyAABB(object);
- // }
- }
- var sorted = object.sortable.sorted || object.childNodes;
- // should account for z-index
- sorted.forEach(function (child) {
- renderByZIndex(child, context);
- });
- };
- // render at the end of frame
- renderingService.hooks.endFrame.tap(CanvasRendererPlugin.tag, function () {
- var context = contextService.getContext();
- // clear & clip dirty rectangle
- var dpr = contextService.getDPR();
- mat4.fromScaling(_this.dprMatrix, [dpr, dpr, 1]);
- mat4.multiply(_this.vpMatrix, _this.dprMatrix, camera.getOrthoMatrix());
- // if (this.clearFullScreen) {
- if (_this.clearFullScreen) {
- // console.log('canvas renderer fcp...');
- renderByZIndex(renderingContext.root, context);
- } else {
- // console.log('canvas renderer next...');
- // merge removed AABB
- var dirtyRenderBounds = _this.safeMergeAABB.apply(_this, [_this.mergeDirtyAABBs(_this.renderQueue)].concat(_this.removedRBushNodeAABBs.map(function (_ref) {
- var minX = _ref.minX,
- minY = _ref.minY,
- maxX = _ref.maxX,
- maxY = _ref.maxY;
- var aabb = new AABB();
- aabb.setMinMax(
- // vec3.fromValues(minX, minY, 0),
- // vec3.fromValues(maxX, maxY, 0),
- [minX, minY, 0], [maxX, maxY, 0]);
- return aabb;
- })));
- _this.removedRBushNodeAABBs = [];
- if (AABB.isEmpty(dirtyRenderBounds)) {
- _this.renderQueue = [];
- return;
- }
- var dirtyRect = _this.convertAABB2Rect(dirtyRenderBounds);
- var x = dirtyRect.x,
- y = dirtyRect.y,
- width = dirtyRect.width,
- height = dirtyRect.height;
- var tl = vec3.transformMat4(_this.vec3a, [x, y, 0], _this.vpMatrix);
- var tr = vec3.transformMat4(_this.vec3b, [x + width, y, 0], _this.vpMatrix);
- var bl = vec3.transformMat4(_this.vec3c, [x, y + height, 0], _this.vpMatrix);
- var br = vec3.transformMat4(_this.vec3d, [x + width, y + height, 0], _this.vpMatrix);
- var minx = Math.min(tl[0], tr[0], br[0], bl[0]);
- var miny = Math.min(tl[1], tr[1], br[1], bl[1]);
- var maxx = Math.max(tl[0], tr[0], br[0], bl[0]);
- var maxy = Math.max(tl[1], tr[1], br[1], bl[1]);
- var ix = Math.floor(minx);
- var iy = Math.floor(miny);
- var iwidth = Math.ceil(maxx - minx);
- var iheight = Math.ceil(maxy - miny);
- context.save();
- _this.clearRect(context, ix, iy, iwidth, iheight, config.background);
- context.beginPath();
- context.rect(ix, iy, iwidth, iheight);
- context.clip();
- // @see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
- context.setTransform(_this.vpMatrix[0], _this.vpMatrix[1], _this.vpMatrix[4], _this.vpMatrix[5], _this.vpMatrix[12], _this.vpMatrix[13]);
- // draw dirty rectangle
- var _config$renderer$getC = config.renderer.getConfig(),
- enableDirtyRectangleRenderingDebug = _config$renderer$getC.enableDirtyRectangleRenderingDebug;
- if (enableDirtyRectangleRenderingDebug) {
- canvas.dispatchEvent(new CustomEvent(CanvasEvent.DIRTY_RECTANGLE, {
- dirtyRect: {
- x: ix,
- y: iy,
- width: iwidth,
- height: iheight
- }
- }));
- }
- // search objects intersect with dirty rectangle
- var dirtyObjects = _this.searchDirtyObjects(dirtyRenderBounds);
- // do rendering
- dirtyObjects
- // sort by z-index
- .sort(function (a, b) {
- return a.sortable.renderOrder - b.sortable.renderOrder;
- }).forEach(function (object) {
- // culled object should not be rendered
- if (object && object.isVisible() && !object.isCulled()) {
- _this.renderDisplayObject(object, context, _this.context, _this.restoreStack, runtime);
- }
- });
- context.restore();
- // save dirty AABBs in last frame
- _this.renderQueue.forEach(function (object) {
- _this.saveDirtyAABB(object);
- });
- // clear queue
- _this.renderQueue = [];
- }
- // pop restore stack, eg. root -> parent -> child
- _this.restoreStack.forEach(function () {
- context.restore();
- });
- // clear restore stack
- _this.restoreStack = [];
- });
- renderingService.hooks.render.tap(CanvasRendererPlugin.tag, function (object) {
- if (!_this.clearFullScreen) {
- // render at the end of frame
- _this.renderQueue.push(object);
- }
- });
- };
- _proto.clearRect = function clearRect(context, x, y, width, height, background) {
- // clearRect is faster than fillRect @see https://stackoverflow.com/a/30830253
- context.clearRect(x, y, width, height);
- if (background) {
- context.fillStyle = background;
- context.fillRect(x, y, width, height);
- }
- };
- _proto.renderDisplayObject = function renderDisplayObject(object, context, canvasContext, restoreStack, runtime) {
- var nodeName = object.nodeName;
- // console.log('canvas render:', object);
- // restore to its ancestor
- var parent = restoreStack[restoreStack.length - 1];
- if (parent && !(object.compareDocumentPosition(parent) & Node.DOCUMENT_POSITION_CONTAINS)) {
- context.restore();
- restoreStack.pop();
- }
- // @ts-ignore
- var styleRenderer = this.context.styleRendererFactory[nodeName];
- var generatePath = this.pathGeneratorFactory[nodeName];
- // clip path
- var clipPath = object.parsedStyle.clipPath;
- if (clipPath) {
- this.applyWorldTransform(context, clipPath);
- // generate path in local space
- var _generatePath = this.pathGeneratorFactory[clipPath.nodeName];
- if (_generatePath) {
- context.save();
- // save clip
- restoreStack.push(object);
- context.beginPath();
- _generatePath(context, clipPath.parsedStyle);
- context.closePath();
- context.clip();
- }
- }
- // fill & stroke
- if (styleRenderer) {
- this.applyWorldTransform(context, object);
- context.save();
- // apply attributes to context
- this.applyAttributesToContext(context, object);
- }
- if (generatePath) {
- context.beginPath();
- generatePath(context, object.parsedStyle);
- if (object.nodeName !== Shape.LINE && object.nodeName !== Shape.PATH && object.nodeName !== Shape.POLYLINE) {
- context.closePath();
- }
- }
- // fill & stroke
- if (styleRenderer) {
- styleRenderer.render(context, object.parsedStyle, object, canvasContext, this, runtime);
- // restore applied attributes, eg. shadowBlur shadowColor...
- context.restore();
- }
- // finish rendering, clear dirty flag
- object.renderable.dirty = false;
- };
- _proto.convertAABB2Rect = function convertAABB2Rect(aabb) {
- var min = aabb.getMin();
- var max = aabb.getMax();
- // expand the rectangle a bit to avoid artifacts
- // @see https://www.yuque.com/antv/ou292n/bi8nix#ExvCu
- var minX = Math.floor(min[0]);
- var minY = Math.floor(min[1]);
- var maxX = Math.ceil(max[0]);
- var maxY = Math.ceil(max[1]);
- var width = maxX - minX;
- var height = maxY - minY;
- return {
- x: minX,
- y: minY,
- width: width,
- height: height
- };
- }
- /**
- * TODO: merge dirty rectangles with some strategies.
- * For now, we just simply merge all the rectangles into one.
- * @see https://idom.me/articles/841.html
- */;
- _proto.mergeDirtyAABBs = function mergeDirtyAABBs(dirtyObjects) {
- // merge into a big AABB
- // TODO: skip descendant if ancestor is caculated, but compareNodePosition is really slow
- var aabb = new AABB();
- dirtyObjects.forEach(function (object) {
- var renderBounds = object.getRenderBounds();
- aabb.add(renderBounds);
- var dirtyRenderBounds = object.renderable.dirtyRenderBounds;
- if (dirtyRenderBounds) {
- aabb.add(dirtyRenderBounds);
- }
- });
- return aabb;
- };
- _proto.searchDirtyObjects = function searchDirtyObjects(dirtyRectangle) {
- // search in r-tree, get all affected nodes
- var _dirtyRectangle$getMi = dirtyRectangle.getMin(),
- minX = _dirtyRectangle$getMi[0],
- minY = _dirtyRectangle$getMi[1];
- var _dirtyRectangle$getMa = dirtyRectangle.getMax(),
- maxX = _dirtyRectangle$getMa[0],
- maxY = _dirtyRectangle$getMa[1];
- var rBushNodes = this.rBush.search({
- minX: minX,
- minY: minY,
- maxX: maxX,
- maxY: maxY
- });
- return rBushNodes.map(function (_ref2) {
- var displayObject = _ref2.displayObject;
- return displayObject;
- });
- };
- _proto.saveDirtyAABB = function saveDirtyAABB(object) {
- var renderable = object.renderable;
- if (!renderable.dirtyRenderBounds) {
- renderable.dirtyRenderBounds = new AABB();
- }
- var renderBounds = object.getRenderBounds();
- if (renderBounds) {
- // save last dirty aabb
- renderable.dirtyRenderBounds.update(renderBounds.center, renderBounds.halfExtents);
- }
- }
- /**
- * TODO: batch the same global attributes
- */;
- _proto.applyAttributesToContext = function applyAttributesToContext(context, object) {
- var _object$parsedStyle = object.parsedStyle,
- stroke = _object$parsedStyle.stroke,
- fill = _object$parsedStyle.fill,
- opacity = _object$parsedStyle.opacity,
- lineDash = _object$parsedStyle.lineDash,
- lineDashOffset = _object$parsedStyle.lineDashOffset;
- // @see https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setLineDash
- if (lineDash) {
- context.setLineDash(lineDash);
- }
- // @see https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
- if (!isNil(lineDashOffset)) {
- context.lineDashOffset = lineDashOffset;
- }
- if (!isNil(opacity)) {
- context.globalAlpha *= opacity;
- }
- if (!isNil(stroke) && !Array.isArray(stroke) && !stroke.isNone) {
- context.strokeStyle = object.attributes.stroke;
- }
- if (!isNil(fill) && !Array.isArray(fill) && !fill.isNone) {
- context.fillStyle = object.attributes.fill;
- }
- };
- _proto.applyWorldTransform = function applyWorldTransform(context, object, matrix) {
- var tx = 0;
- var ty = 0;
- var _ref3 = object.parsedStyle || {},
- anchor = _ref3.anchor;
- var anchorX = anchor && anchor[0] || 0;
- var anchorY = anchor && anchor[1] || 0;
- if (anchorX !== 0 || anchorY !== 0) {
- // const bounds = object.getGeometryBounds();
- var bounds = object.geometry.contentBounds;
- var width = bounds && bounds.halfExtents[0] * 2 || 0;
- var height = bounds && bounds.halfExtents[1] * 2 || 0;
- tx = -(anchorX * width);
- ty = -(anchorY * height);
- }
- // apply clip shape's RTS
- if (matrix) {
- mat4.copy(this.tmpMat4, object.getLocalTransform());
- this.vec3a[0] = tx;
- this.vec3a[1] = ty;
- this.vec3a[2] = 0;
- mat4.translate(this.tmpMat4, this.tmpMat4, this.vec3a);
- mat4.multiply(this.tmpMat4, matrix, this.tmpMat4);
- mat4.multiply(this.tmpMat4, this.vpMatrix, this.tmpMat4);
- } else {
- // apply RTS transformation in world space
- mat4.copy(this.tmpMat4, object.getWorldTransform());
- this.vec3a[0] = tx;
- this.vec3a[1] = ty;
- this.vec3a[2] = 0;
- mat4.translate(this.tmpMat4, this.tmpMat4, this.vec3a);
- mat4.multiply(this.tmpMat4, this.vpMatrix, this.tmpMat4);
- }
- // @see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
- context.setTransform(this.tmpMat4[0], this.tmpMat4[1], this.tmpMat4[4], this.tmpMat4[5], this.tmpMat4[12], this.tmpMat4[13]);
- };
- _proto.safeMergeAABB = function safeMergeAABB() {
- var merged = new AABB();
- for (var _len = arguments.length, aabbs = new Array(_len), _key = 0; _key < _len; _key++) {
- aabbs[_key] = arguments[_key];
- }
- aabbs.forEach(function (aabb) {
- merged.add(aabb);
- });
- return merged;
- };
- return CanvasRendererPlugin;
- }();
- CanvasRendererPlugin.tag = 'CanvasRenderer';
- var DefaultRenderer = /*#__PURE__*/function () {
- function DefaultRenderer(imagePool) {
- this.imagePool = void 0;
- this.imagePool = imagePool;
- }
- var _proto = DefaultRenderer.prototype;
- _proto.render = function render(context, parsedStyle, object, canvasContext, plugin, runtime) {
- var fill = parsedStyle.fill,
- fillRule = parsedStyle.fillRule,
- opacity = parsedStyle.opacity,
- fillOpacity = parsedStyle.fillOpacity,
- stroke = parsedStyle.stroke,
- strokeOpacity = parsedStyle.strokeOpacity,
- lineWidth = parsedStyle.lineWidth,
- lineCap = parsedStyle.lineCap,
- lineJoin = parsedStyle.lineJoin,
- shadowType = parsedStyle.shadowType,
- shadowColor = parsedStyle.shadowColor,
- shadowBlur = parsedStyle.shadowBlur,
- filter = parsedStyle.filter,
- miterLimit = parsedStyle.miterLimit;
- var hasFill = !isNil(fill) && !fill.isNone;
- var hasStroke = !isNil(stroke) && !stroke.isNone && lineWidth > 0;
- var isFillTransparent = fill.alpha === 0;
- var hasFilter = !!(filter && filter.length);
- var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
- var nodeName = object.nodeName;
- var isInnerShadow = shadowType === 'inner';
- var shouldDrawShadowWithStroke = hasStroke && hasShadow && (nodeName === Shape.PATH || nodeName === Shape.LINE || nodeName === Shape.POLYLINE || isFillTransparent || isInnerShadow);
- if (hasFill) {
- context.globalAlpha = opacity * fillOpacity;
- if (!shouldDrawShadowWithStroke) {
- setShadowAndFilter(object, context, hasShadow);
- }
- this.fill(context, object, fill, fillRule, canvasContext, plugin, runtime);
- if (!shouldDrawShadowWithStroke) {
- this.clearShadowAndFilter(context, hasFilter, hasShadow);
- }
- }
- if (hasStroke) {
- context.globalAlpha = opacity * strokeOpacity;
- context.lineWidth = lineWidth;
- if (!isNil(miterLimit)) {
- context.miterLimit = miterLimit;
- }
- if (!isNil(lineCap)) {
- context.lineCap = lineCap;
- }
- if (!isNil(lineJoin)) {
- context.lineJoin = lineJoin;
- }
- if (shouldDrawShadowWithStroke) {
- if (isInnerShadow) {
- context.globalCompositeOperation = 'source-atop';
- }
- setShadowAndFilter(object, context, true);
- if (isInnerShadow) {
- this.stroke(context, object, stroke, canvasContext, plugin, runtime);
- context.globalCompositeOperation = 'source-over';
- this.clearShadowAndFilter(context, hasFilter, true);
- }
- }
- this.stroke(context, object, stroke, canvasContext, plugin, runtime);
- }
- };
- _proto.clearShadowAndFilter = function clearShadowAndFilter(context, hasFilter, hasShadow) {
- if (hasShadow) {
- context.shadowColor = 'transparent';
- context.shadowBlur = 0;
- }
- if (hasFilter) {
- // save drop-shadow filter
- var oldFilter = context.filter;
- if (!isNil(oldFilter) && oldFilter.indexOf('drop-shadow') > -1) {
- context.filter = oldFilter.replace(/drop-shadow\([^)]*\)/, '').trim() || 'none';
- }
- }
- };
- _proto.fill = function fill(context, object, _fill, fillRule, canvasContext, plugin, runtime) {
- var _this = this;
- if (Array.isArray(_fill)) {
- _fill.forEach(function (gradient) {
- context.fillStyle = _this.getColor(gradient, object, context);
- fillRule ? context.fill(fillRule) : context.fill();
- });
- } else {
- if (isPattern(_fill)) {
- context.fillStyle = this.getPattern(_fill, object, context, canvasContext, plugin, runtime);
- }
- fillRule ? context.fill(fillRule) : context.fill();
- }
- };
- _proto.stroke = function stroke(context, object, _stroke, canvasContext, plugin, runtime) {
- var _this2 = this;
- if (Array.isArray(_stroke)) {
- _stroke.forEach(function (gradient) {
- context.strokeStyle = _this2.getColor(gradient, object, context);
- context.stroke();
- });
- } else {
- if (isPattern(_stroke)) {
- context.strokeStyle = this.getPattern(_stroke, object, context, canvasContext, plugin, runtime);
- }
- context.stroke();
- }
- };
- _proto.getPattern = function getPattern(pattern, object, context, canvasContext, plugin, runtime) {
- var $offscreenCanvas;
- var dpr;
- if (pattern.image.nodeName === 'rect') {
- var _pattern$image$parsed = pattern.image.parsedStyle,
- width = _pattern$image$parsed.width,
- height = _pattern$image$parsed.height;
- dpr = canvasContext.contextService.getDPR();
- var offscreenCanvas = canvasContext.config.offscreenCanvas;
- $offscreenCanvas = runtime.offscreenCanvas.getOrCreateCanvas(offscreenCanvas);
- $offscreenCanvas.width = width * dpr;
- $offscreenCanvas.height = height * dpr;
- var offscreenCanvasContext = runtime.offscreenCanvas.getOrCreateContext(offscreenCanvas);
- var restoreStack = [];
- // offscreenCanvasContext.scale(1 / dpr, 1 / dpr);
- pattern.image.forEach(function (object) {
- plugin.renderDisplayObject(object, offscreenCanvasContext, canvasContext, restoreStack, runtime);
- });
- restoreStack.forEach(function () {
- offscreenCanvasContext.restore();
- });
- }
- var canvasPattern = this.imagePool.getOrCreatePatternSync(pattern, context, $offscreenCanvas, dpr, function () {
- // set dirty rectangle flag
- object.renderable.dirty = true;
- canvasContext.renderingService.dirtify();
- });
- return canvasPattern;
- };
- _proto.getColor = function getColor(parsedColor, object, context) {
- var color;
- if (parsedColor.type === GradientType.LinearGradient || parsedColor.type === GradientType.RadialGradient) {
- var bounds = object.getGeometryBounds();
- var width = bounds && bounds.halfExtents[0] * 2 || 1;
- var height = bounds && bounds.halfExtents[1] * 2 || 1;
- color = this.imagePool.getOrCreateGradient(_extends({
- type: parsedColor.type
- }, parsedColor.value, {
- width: width,
- height: height
- }), context);
- }
- return color;
- };
- return DefaultRenderer;
- }();
- /**
- * apply before fill and stroke but only once
- */
- function setShadowAndFilter(object, context, hasShadow) {
- var _object$parsedStyle = object.parsedStyle,
- filter = _object$parsedStyle.filter,
- shadowColor = _object$parsedStyle.shadowColor,
- shadowBlur = _object$parsedStyle.shadowBlur,
- shadowOffsetX = _object$parsedStyle.shadowOffsetX,
- shadowOffsetY = _object$parsedStyle.shadowOffsetY;
- if (filter && filter.length) {
- // use raw filter string
- context.filter = object.style.filter;
- }
- if (hasShadow) {
- context.shadowColor = shadowColor.toString();
- context.shadowBlur = shadowBlur || 0;
- context.shadowOffsetX = shadowOffsetX || 0;
- context.shadowOffsetY = shadowOffsetY || 0;
- }
- }
- var ImageRenderer = /*#__PURE__*/function () {
- function ImageRenderer(imagePool) {
- this.imagePool = void 0;
- this.imagePool = imagePool;
- }
- var _proto = ImageRenderer.prototype;
- _proto.render = function render(context, parsedStyle, object) {
- var width = parsedStyle.width,
- height = parsedStyle.height,
- img = parsedStyle.img,
- shadowColor = parsedStyle.shadowColor,
- shadowBlur = parsedStyle.shadowBlur;
- var image;
- var iw = width;
- var ih = height;
- if (isString(img)) {
- // image has been loaded in `mounted` hook
- image = this.imagePool.getImageSync(img);
- } else {
- iw || (iw = img.width);
- ih || (ih = img.height);
- image = img;
- }
- if (image) {
- var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
- setShadowAndFilter(object, context, hasShadow);
- // node-canvas will throw the following err:
- // Error: Image given has not completed loading
- try {
- context.drawImage(image, 0, 0, iw, ih);
- } catch (e) {}
- }
- };
- return ImageRenderer;
- }();
- var TextRenderer = /*#__PURE__*/function () {
- function TextRenderer() {}
- var _proto = TextRenderer.prototype;
- _proto.render = function render(context, parsedStyle, object) {
- var lineWidth = parsedStyle.lineWidth,
- textAlign = parsedStyle.textAlign,
- textBaseline = parsedStyle.textBaseline,
- lineJoin = parsedStyle.lineJoin,
- miterLimit = parsedStyle.miterLimit,
- letterSpacing = parsedStyle.letterSpacing,
- stroke = parsedStyle.stroke,
- fill = parsedStyle.fill,
- fillOpacity = parsedStyle.fillOpacity,
- strokeOpacity = parsedStyle.strokeOpacity,
- opacity = parsedStyle.opacity,
- metrics = parsedStyle.metrics,
- dx = parsedStyle.dx,
- dy = parsedStyle.dy,
- shadowColor = parsedStyle.shadowColor,
- shadowBlur = parsedStyle.shadowBlur;
- var font = metrics.font,
- lines = metrics.lines,
- height = metrics.height,
- lineHeight = metrics.lineHeight,
- lineMetrics = metrics.lineMetrics;
- context.font = font;
- context.lineWidth = lineWidth;
- context.textAlign = textAlign === 'middle' ? 'center' : textAlign;
- var formattedTextBaseline = textBaseline;
- if (
- // formattedTextBaseline === 'bottom' ||
- !runtime.enableCSSParsing && formattedTextBaseline === 'alphabetic') {
- formattedTextBaseline = 'bottom';
- }
- context.lineJoin = lineJoin;
- if (!isNil(miterLimit)) {
- context.miterLimit = miterLimit;
- }
- var linePositionY = 0;
- // handle vertical text baseline
- if (textBaseline === 'middle') {
- linePositionY = -height / 2 - lineHeight / 2;
- } else if (textBaseline === 'bottom' || textBaseline === 'alphabetic' || textBaseline === 'ideographic') {
- linePositionY = -height;
- } else if (textBaseline === 'top' || textBaseline === 'hanging') {
- linePositionY = -lineHeight;
- }
- // account for dx & dy
- var offsetX = dx || 0;
- linePositionY += dy || 0;
- if (lines.length === 1) {
- if (formattedTextBaseline === 'bottom') {
- formattedTextBaseline = 'middle';
- linePositionY -= 0.5 * height;
- } else if (formattedTextBaseline === 'top') {
- formattedTextBaseline = 'middle';
- linePositionY += 0.5 * height;
- }
- }
- context.textBaseline = formattedTextBaseline;
- var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
- setShadowAndFilter(object, context, hasShadow);
- // draw lines line by line
- for (var i = 0; i < lines.length; i++) {
- var linePositionX = lineWidth / 2 + offsetX;
- linePositionY += lineHeight;
- // no need to re-position X, cause we already set text align
- // @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign
- if (!isNil(stroke) && !stroke.isNone && lineWidth) {
- this.drawLetterSpacing(context, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, fillOpacity, strokeOpacity, opacity, true);
- }
- if (!isNil(fill)) {
- this.drawLetterSpacing(context, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, fillOpacity, strokeOpacity, opacity);
- }
- }
- };
- _proto.drawLetterSpacing = function drawLetterSpacing(context, text, lineMetrics, textAlign, x, y, letterSpacing, fillOpacity, strokeOpacity, opacity, isStroke) {
- if (isStroke === void 0) {
- isStroke = false;
- }
- // letterSpacing of 0 means normal, render all texts directly
- if (letterSpacing === 0) {
- if (isStroke) {
- this.strokeText(context, text, x, y, strokeOpacity);
- } else {
- this.fillText(context, text, x, y, fillOpacity, opacity);
- }
- return;
- }
- // draw text using left align
- var currentTextAlign = context.textAlign;
- context.textAlign = 'left';
- var currentPosition = x;
- if (textAlign === 'center' || textAlign === 'middle') {
- currentPosition = x - lineMetrics.width / 2;
- } else if (textAlign === 'right' || textAlign === 'end') {
- currentPosition = x - lineMetrics.width;
- }
- var stringArray = Array.from(text);
- var previousWidth = context.measureText(text).width;
- var currentWidth = 0;
- for (var i = 0; i < stringArray.length; ++i) {
- var currentChar = stringArray[i];
- if (isStroke) {
- this.strokeText(context, currentChar, currentPosition, y, strokeOpacity);
- } else {
- this.fillText(context, currentChar, currentPosition, y, fillOpacity, opacity);
- }
- currentWidth = context.measureText(text.substring(i + 1)).width;
- currentPosition += previousWidth - currentWidth + letterSpacing;
- previousWidth = currentWidth;
- }
- context.textAlign = currentTextAlign;
- };
- _proto.fillText = function fillText(context, text, x, y, fillOpacity, opacity) {
- var currentGlobalAlpha;
- var applyOpacity = !isNil(fillOpacity) && fillOpacity !== 1;
- if (applyOpacity) {
- currentGlobalAlpha = context.globalAlpha;
- context.globalAlpha = fillOpacity * opacity;
- }
- context.fillText(text, x, y);
- if (applyOpacity) {
- context.globalAlpha = currentGlobalAlpha;
- }
- };
- _proto.strokeText = function strokeText(context, text, x, y, strokeOpacity) {
- var currentGlobalAlpha;
- var applyOpacity = !isNil(strokeOpacity) && strokeOpacity !== 1;
- if (applyOpacity) {
- currentGlobalAlpha = context.globalAlpha;
- context.globalAlpha = strokeOpacity;
- }
- context.strokeText(text, x, y);
- if (applyOpacity) {
- context.globalAlpha = currentGlobalAlpha;
- }
- };
- return TextRenderer;
- }();
- var RectRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(RectRenderer, _DefaultRenderer);
- function RectRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return RectRenderer;
- }(DefaultRenderer);
- var CircleRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(CircleRenderer, _DefaultRenderer);
- function CircleRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return CircleRenderer;
- }(DefaultRenderer);
- var EllipseRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(EllipseRenderer, _DefaultRenderer);
- function EllipseRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return EllipseRenderer;
- }(DefaultRenderer);
- var LineRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(LineRenderer, _DefaultRenderer);
- function LineRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return LineRenderer;
- }(DefaultRenderer);
- var PolylineRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(PolylineRenderer, _DefaultRenderer);
- function PolylineRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return PolylineRenderer;
- }(DefaultRenderer);
- var PolygonRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(PolygonRenderer, _DefaultRenderer);
- function PolygonRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return PolygonRenderer;
- }(DefaultRenderer);
- var PathRenderer = /*#__PURE__*/function (_DefaultRenderer) {
- _inheritsLoose(PathRenderer, _DefaultRenderer);
- function PathRenderer() {
- return _DefaultRenderer.apply(this, arguments) || this;
- }
- return PathRenderer;
- }(DefaultRenderer);
- var Plugin = /*#__PURE__*/function (_AbstractRendererPlug) {
- _inheritsLoose(Plugin, _AbstractRendererPlug);
- function Plugin(options) {
- var _this;
- if (options === void 0) {
- options = {};
- }
- _this = _AbstractRendererPlug.call(this) || this;
- _this.options = void 0;
- _this.name = 'canvas-renderer';
- _this.options = options;
- return _this;
- }
- var _proto = Plugin.prototype;
- _proto.init = function init() {
- var _defaultStyleRenderer;
- var canvasRendererPluginOptions = _extends({
- dirtyObjectNumThreshold: 500,
- dirtyObjectRatioThreshold: 0.8
- }, this.options);
- // @ts-ignore
- var imagePool = this.context.imagePool;
- var defaultRenderer = new DefaultRenderer(imagePool);
- var defaultStyleRendererFactory = (_defaultStyleRenderer = {}, _defaultStyleRenderer[Shape.CIRCLE] = defaultRenderer, _defaultStyleRenderer[Shape.ELLIPSE] = defaultRenderer, _defaultStyleRenderer[Shape.RECT] = defaultRenderer, _defaultStyleRenderer[Shape.IMAGE] = new ImageRenderer(imagePool), _defaultStyleRenderer[Shape.TEXT] = new TextRenderer(), _defaultStyleRenderer[Shape.LINE] = defaultRenderer, _defaultStyleRenderer[Shape.POLYLINE] = defaultRenderer, _defaultStyleRenderer[Shape.POLYGON] = defaultRenderer, _defaultStyleRenderer[Shape.PATH] = defaultRenderer, _defaultStyleRenderer[Shape.GROUP] = undefined, _defaultStyleRenderer[Shape.HTML] = undefined, _defaultStyleRenderer[Shape.MESH] = undefined, _defaultStyleRenderer);
- this.context.defaultStyleRendererFactory = defaultStyleRendererFactory;
- this.context.styleRendererFactory = defaultStyleRendererFactory;
- this.addRenderingPlugin(new CanvasRendererPlugin(canvasRendererPluginOptions));
- };
- _proto.destroy = function destroy() {
- this.removeAllRenderingPlugins();
- delete this.context.defaultStyleRendererFactory;
- delete this.context.styleRendererFactory;
- };
- return Plugin;
- }(AbstractRendererPlugin);
- export { CircleRenderer, EllipseRenderer, ImageRenderer, LineRenderer, PathRenderer, Plugin, PolygonRenderer, PolylineRenderer, RectRenderer, TextRenderer };
|