/** * @class Ext.chart.series.sprite.Box * @extends Ext.draw.sprite.Sprite * * A sprite that represents a 3D bar or column. * Used as an item template by the {@link Ext.chart.series.sprite.Bar3D} marker holder. * */Ext.define('Ext.chart.series.sprite.Box', { extend: 'Ext.draw.sprite.Sprite', alias: 'sprite.box', type: 'box', inheritableStatics: { def: { processors: { /** * @cfg {Number} [x=0] * The position of the sprite on the x-axis. * Corresponds to the center of the front face of the box. */ x: 'number', /** * @cfg {Number} [y=0] * The position of the sprite on the y-axis. * Corresponds to the top of the front face of the box. */ y: 'number', /** * @cfg {Number} [width=8] The width of the box. */ width: 'number', /** * @cfg {Number} [height=8] The height of the box. */ height: 'number', /** * @cfg {Number} [depth=8] The depth of the box. */ depth: 'number', /** * @cfg {String} [orientation='vertical'] The orientation of the box. */ orientation: 'enums(vertical,horizontal)', /** * @cfg {Boolean} [showStroke=false] * Whether to render the stroke or not. */ showStroke: 'bool', /** * @cfg {Number} [saturationFactor=1] * The factor applied to the saturation of the box. */ saturationFactor: 'number', /** * @cfg {Number} [brightnessFactor=1] * The factor applied to the brightness of the box. */ brightnessFactor: 'number', /** * @cfg {Number} [colorSpread=1] * An attribute used to control how flat the bar gradient looks. * A value of 0 essentially means no gradient (flat color). */ colorSpread: 'number' }, triggers: { x: 'bbox', y: 'bbox', width: 'bbox', height: 'bbox', depth: 'bbox', orientation: 'bbox' }, defaults: { x: 0, y: 0, width: 8, height: 8, depth: 8, orientation: 'vertical', showStroke: false, saturationFactor: 1, brightnessFactor: 1, colorSpread: 1, lineJoin: 'bevel' } } }, constructor: function (config) { this.callParent([config]); this.topGradient = new Ext.draw.gradient.Linear({}); this.rightGradient = new Ext.draw.gradient.Linear({}); this.frontGradient = new Ext.draw.gradient.Linear({}); }, updatePlainBBox: function (plain) { var attr = this.attr, x = attr.x, y = attr.y, width = attr.width, height = attr.height, depth = attr.depth; plain.x = x - width * 0.5; plain.width = width + depth; if (height > 0) { plain.y = y; plain.height = height + depth; } else { plain.y = y + depth; plain.height = height - depth; } }, render: function (surface, ctx) { var me = this, attr = me.attr, center = attr.x, top = attr.y, bottom = top + attr.height, isNegative = top < bottom, halfWidth = attr.width * 0.5, depth = attr.depth, isHorizontal = attr.orientation === 'horizontal', isTransparent = attr.globalAlpha < 1, fillStyle = attr.fillStyle, color = Ext.util.Color.create( fillStyle.isGradient ? fillStyle.getStops()[0].color : fillStyle ), saturationFactor = attr.saturationFactor, brightnessFactor = attr.brightnessFactor, colorSpread = attr.colorSpread, hsv = color.getHSV(), bbox = {}, roundX, roundY, temp; if (!attr.showStroke) { ctx.strokeStyle = Ext.util.Color.RGBA_NONE; } if (isNegative) { temp = top; top = bottom; bottom = temp; } // Refresh gradients based on sprite's fillStyle and other attributes. me.topGradient.setDegrees(isHorizontal ? 0 : 80); me.topGradient.setStops([ { offset: 0, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1), Ext.Number.constrain((0.5 + colorSpread * 0.10) * brightnessFactor, 0, 1) ) }, { offset: 1, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1), Ext.Number.constrain((0.5 - colorSpread * 0.11) * brightnessFactor, 0, 1) ) } ]); me.rightGradient.setDegrees(isHorizontal ? 45 : 90); me.rightGradient.setStops([ { offset: 0, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1), Ext.Number.constrain((0.5 - colorSpread * 0.14) * brightnessFactor, 0, 1) ) }, { offset: 1, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * (1.0 + colorSpread * 0.4) * saturationFactor, 0, 1), Ext.Number.constrain((0.5 - colorSpread * 0.32) * brightnessFactor, 0, 1) ) } ]); if (isHorizontal) { me.frontGradient.setDegrees(0); // 0° angle looks like 90° angle because the chart is flipped } else { me.frontGradient.setRadians(Math.atan2(top - bottom, halfWidth * 2)); } me.frontGradient.setStops([ { offset: 0, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * (1.0 - colorSpread * 0.1) * saturationFactor, 0, 1), Ext.Number.constrain((0.5 + colorSpread * 0.1) * brightnessFactor, 0, 1) ) }, { offset: 1, color: Ext.util.Color.fromHSV( hsv[0], Ext.Number.constrain(hsv[1] * (1.0 + colorSpread * 0.1) * saturationFactor, 0, 1), Ext.Number.constrain((0.5 - colorSpread * 0.23) * brightnessFactor, 0, 1) ) } ]); if (isTransparent || isNegative) { // Bottom side. ctx.beginPath(); ctx.moveTo(center - halfWidth, bottom); ctx.lineTo(center - halfWidth + depth, bottom + depth); ctx.lineTo(center + halfWidth + depth, bottom + depth); ctx.lineTo(center + halfWidth, bottom); ctx.closePath(); bbox.x = center - halfWidth; bbox.y = top; bbox.width = halfWidth + depth; bbox.height = depth; ctx.fillStyle = (isHorizontal ? me.rightGradient : me.topGradient).generateGradient(ctx, bbox); ctx.fillStroke(attr); } if (isTransparent) { // Left side. ctx.beginPath(); ctx.moveTo(center - halfWidth, top); ctx.lineTo(center - halfWidth + depth, top + depth); ctx.lineTo(center - halfWidth + depth, bottom + depth); ctx.lineTo(center - halfWidth, bottom); ctx.closePath(); bbox.x = center + halfWidth; bbox.y = bottom; bbox.width = depth; bbox.height = top + depth - bottom; ctx.fillStyle = (isHorizontal ? me.topGradient : me.rightGradient).generateGradient(ctx, bbox); ctx.fillStroke(attr); } // Top side. roundY = surface.roundPixel(top); ctx.beginPath(); ctx.moveTo(center - halfWidth, roundY); ctx.lineTo(center - halfWidth + depth, top + depth); ctx.lineTo(center + halfWidth + depth, top + depth); ctx.lineTo(center + halfWidth, roundY); ctx.closePath(); bbox.x = center - halfWidth; bbox.y = top; bbox.width = halfWidth + depth; bbox.height = depth; ctx.fillStyle = (isHorizontal ? me.rightGradient : me.topGradient).generateGradient(ctx, bbox); ctx.fillStroke(attr); // Right side. roundX = surface.roundPixel(center + halfWidth); ctx.beginPath(); ctx.moveTo(roundX, surface.roundPixel(top)); ctx.lineTo(center + halfWidth + depth, top + depth); ctx.lineTo(center + halfWidth + depth, bottom + depth); ctx.lineTo(roundX, bottom); ctx.closePath(); bbox.x = center + halfWidth; bbox.y = bottom; bbox.width = depth; bbox.height = top + depth - bottom; ctx.fillStyle = (isHorizontal ? me.topGradient : me.rightGradient).generateGradient(ctx, bbox); ctx.fillStroke(attr); // Front side. roundX = surface.roundPixel(center + halfWidth); roundY = surface.roundPixel(top); ctx.beginPath(); ctx.moveTo(center - halfWidth, bottom); ctx.lineTo(center - halfWidth, roundY); ctx.lineTo(roundX, roundY); ctx.lineTo(roundX, bottom); ctx.closePath(); bbox.x = center - halfWidth; bbox.y = bottom; bbox.width = halfWidth * 2; bbox.height = top - bottom; ctx.fillStyle = me.frontGradient.generateGradient(ctx, bbox); ctx.fillStroke(attr); } });