UNPKG

highcharts

Version:
189 lines (188 loc) 6.02 kB
/* * * * (c) 2010-2025 Torstein Honsi * * Extensions to the SVGRenderer class to enable 3D shapes * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import Color from '../../Color/Color.js'; const { parse: color } = Color; import RendererRegistry from '../RendererRegistry.js'; const { Element: SVGElement } = RendererRegistry.getRendererType().prototype; import U from '../../Utilities.js'; const { defined, pick } = U; /* * * * Class * * */ class SVGElement3D extends SVGElement { constructor() { /* * * * Static Properties * * */ super(...arguments); /* * * * Properties * * */ this.parts = ['front', 'top', 'side']; this.pathType = 'cuboid'; } /* * * * Functions * * */ /** * The init is used by base - renderer.Element * @private */ initArgs(args) { const elem3d = this, renderer = elem3d.renderer, paths = renderer[elem3d.pathType + 'Path'](args), zIndexes = paths.zIndexes; // Build parts for (const part of elem3d.parts) { const attribs = { 'class': 'highcharts-3d-' + part, zIndex: zIndexes[part] || 0 }; if (renderer.styledMode) { if (part === 'top') { attribs.filter = 'url(#highcharts-brighter)'; } else if (part === 'side') { attribs.filter = 'url(#highcharts-darker)'; } } elem3d[part] = renderer.path(paths[part]) .attr(attribs) .add(elem3d); } elem3d.attr({ 'stroke-linejoin': 'round', zIndex: zIndexes.group }); // Store information if any side of element was rendered by force. elem3d.forcedSides = paths.forcedSides; } /** * Single property setter that applies options to each part * @private */ singleSetterForParts(prop, val, values, verb, duration, complete) { const elem3d = this, newAttr = {}, optionsToApply = [null, null, (verb || 'attr'), duration, complete], hasZIndexes = values?.zIndexes; if (!values) { newAttr[prop] = val; optionsToApply[0] = newAttr; } else { // It is needed to deal with the whole group zIndexing // in case of graph rotation if (hasZIndexes?.group) { elem3d.attr({ zIndex: hasZIndexes.group }); } for (const part of Object.keys(values)) { newAttr[part] = {}; newAttr[part][prop] = values[part]; // Include zIndexes if provided if (hasZIndexes) { newAttr[part].zIndex = values.zIndexes[part] || 0; } } optionsToApply[1] = newAttr; } return this.processParts.apply(elem3d, optionsToApply); } /** * Calls function for each part. Used for attr, animate and destroy. * @private */ processParts(props, partsProps, verb, duration, complete) { const elem3d = this; for (const part of elem3d.parts) { // If different props for different parts if (partsProps) { props = pick(partsProps[part], false); } // Only if something to set, but allow undefined if (props !== false) { elem3d[part][verb](props, duration, complete); } } return elem3d; } /** * Destroy all parts * @private */ destroy() { this.processParts(null, null, 'destroy'); return super.destroy(); } // Following functions are SVGElement3DCuboid (= base) attr(args, val, complete, continueAnimation) { // Resolve setting attributes by string name if (typeof args === 'string' && typeof val !== 'undefined') { const key = args; args = {}; args[key] = val; } if (args.shapeArgs || defined(args.x)) { return this.singleSetterForParts('d', null, this.renderer[this.pathType + 'Path'](args.shapeArgs || args)); } return super.attr(args, void 0, complete, continueAnimation); } animate(args, duration, complete) { if (defined(args.x) && defined(args.y)) { const paths = this.renderer[this.pathType + 'Path'](args), forcedSides = paths.forcedSides; this.singleSetterForParts('d', null, paths, 'animate', duration, complete); this.attr({ zIndex: paths.zIndexes.group }); // If sides that are forced to render changed, recalculate colors. if (forcedSides !== this.forcedSides) { this.forcedSides = forcedSides; if (!this.renderer.styledMode) { this.fillSetter(this.fill); } } } else { super.animate(args, duration, complete); } return this; } fillSetter(fill) { const elem3d = this; elem3d.forcedSides = elem3d.forcedSides || []; elem3d.singleSetterForParts('fill', null, { front: fill, // Do not change color if side was forced to render. top: color(fill).brighten(elem3d.forcedSides.indexOf('top') >= 0 ? 0 : 0.1).get(), side: color(fill).brighten(elem3d.forcedSides.indexOf('side') >= 0 ? 0 : -0.1).get() }); // Fill for animation getter (#6776) elem3d.color = elem3d.fill = fill; return elem3d; } } SVGElement3D.types = { base: SVGElement3D, cuboid: SVGElement3D }; /* * * * Default Export * * */ export default SVGElement3D;