Skip to content

Commit

Permalink
feat: support set gradient color in default. Closed #243.
Browse files Browse the repository at this point in the history
  • Loading branch information
simaQ committed Aug 19, 2018
1 parent 169a0eb commit 20b18a9
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 5 deletions.
14 changes: 10 additions & 4 deletions src/graphic/element.js
@@ -1,6 +1,7 @@
const Util = require('../util/common');
const MatrixUtil = require('./util/matrix');
const Vector2 = require('./util/vector2');
const StyleUtil = require('./util/style-parse');

function isUnchanged(m) {
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
Expand Down Expand Up @@ -170,11 +171,14 @@ class Element {

resetContext(context) {
const elAttrs = this._attrs.attrs;
if (!this.get('isGroup')) {
if (!this._attrs.isGroup) {
for (const k in elAttrs) {
if (SHAPE_ATTRS.indexOf(k) > -1) {
const v = elAttrs[k];
if (k === 'lineDash' && context.setLineDash && v) {
let v = elAttrs[k];
if (k === 'fillStyle' || k === 'strokeStyle') {
v = StyleUtil.parseStyle(v, this, context);
}
if (k === 'lineDash' && context.setLineDash && Util.isArray(v)) {
context.setLineDash(v);
} else {
context[k] = v;
Expand Down Expand Up @@ -246,7 +250,9 @@ class Element {
minX: 0,
maxX: 0,
minY: 0,
maxY: 0
maxY: 0,
width: 0,
height: 0
};
}

Expand Down
107 changes: 107 additions & 0 deletions src/graphic/util/style-parse.js
@@ -0,0 +1,107 @@
const Util = require('../../util/common');

function _addStop(steps, gradient) {
Util.each(steps, item => {
item = item.split(':');
gradient.addColorStop(item[0], item[1]);
});
}

// the string format: 'l(0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff'
function _parseLineGradient(color, shape, context) {
const arr = color.split(' ');
let angle = arr[0].slice(2, arr[0].length - 1);
angle = (angle * Math.PI) / 180;
const steps = arr.slice(1);

const { minX, minY, maxX, maxY } = shape.getBBox();
let start;
let end;

if (angle >= 0 && angle < 0.5 * Math.PI) {
start = {
x: minX,
y: minY
};
end = {
x: maxX,
y: maxY
};
} else if (0.5 * Math.PI <= angle && angle < Math.PI) {
start = {
x: maxX,
y: minY
};
end = {
x: minX,
y: maxY
};
} else if (Math.PI <= angle && angle < 1.5 * Math.PI) {
start = {
x: maxX,
y: maxY
};
end = {
x: minX,
y: minY
};
} else {
start = {
x: minX,
y: maxY
};
end = {
x: maxX,
y: minY
};
}

const tanTheta = Math.tan(angle);
const tanTheta2 = tanTheta * tanTheta;

const x = ((end.x - start.x) + tanTheta * (end.y - start.y)) / (tanTheta2 + 1) + start.x;
const y = tanTheta * ((end.x - start.x) + tanTheta * (end.y - start.y)) / (tanTheta2 + 1) + start.y;
const gradient = context.createLinearGradient(start.x, start.y, x, y);
_addStop(steps, gradient);
return gradient;
}

// the string format: 'r(0.5, 0.5, 0.1) 0:#ffffff 1:#1890ff'
function _parseRadialGradient(color, shape, context) {
const arr = color.split(' ');
let circleCfg = arr[0].slice(2, arr[0].length - 1);
circleCfg = circleCfg.split(',');
const fx = parseFloat(circleCfg[0]);
const fy = parseFloat(circleCfg[1]);
const fr = parseFloat(circleCfg[2]);
const steps = arr.slice(1);
// if radius is 0, no gradient, stroke with the last color
if (fr === 0) {
const color = steps[steps.length - 1];
return color.split(':')[1];
}
const { width, height, minX, minY } = shape.getBBox();
const r = Math.sqrt(width * width + height * height) / 2;
const gradient = context.createRadialGradient(minX + width * fx, minY + height * fy, fr * r, minX + width / 2, minY + height / 2, r);
_addStop(steps, gradient);
return gradient;
}

module.exports = {
parseStyle(color, shape, context) {
if (color[1] === '(') {
try {
const firstCode = color[0];
if (firstCode === 'l') {
return _parseLineGradient(color, shape, context);
} else if (firstCode === 'r') {
return _parseRadialGradient(color, shape, context);
}
} catch (ev) {
console.error('error in parsing gradient string, please check if there are any extra whitespaces.');
console.error(ev);
}
}
return color;
}
};
32 changes: 31 additions & 1 deletion test/unit/graphic/element-spec.js
@@ -1,6 +1,11 @@
const expect = require('chai').expect;
const Element = require('../../../src/graphic/element');

const dom = document.createElement('canvas');
dom.id = 'element';
document.body.appendChild(dom);
const context = dom.getContext('2d');

describe('Element', function() {
let e;
it('constructor', function() {
Expand Down Expand Up @@ -28,6 +33,7 @@ describe('Element', function() {
});

it('attr(key, value), fill', function() {

e.attr('fill', '#333333');
expect(e.attr('fill')).to.equal('#333333');
expect(e.get('attrs').fillStyle).to.equal('#333333');
Expand All @@ -47,6 +53,28 @@ describe('Element', function() {
expect(e.get('attrs').strokeStyle).to.equal('#999');
});

it('attr(key, value), fill a radial gradient', function() {
e.attr('fill', 'r(0,0,1) 0:#ffffff 0.5:#7ec2f3 1:#1890ff');
const fill = e.attr('fillStyle');
expect(fill).to.equal('r(0,0,1) 0:#ffffff 0.5:#7ec2f3 1:#1890ff');

e.resetContext(context);
expect(context.fillStyle).to.be.an.instanceof(CanvasGradient);

e.attr('fill', 'r(0.5,0.5,0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff');
e.resetContext(context);
expect(context.fillStyle).to.equal('#1890ff');
});

it('attr(key, value), stroke a linear gradient', function() {
e.attr('stroke', 'l(0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff');
const stroke = e.attr('strokeStyle');
expect(stroke).to.equal('l(0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff');

e.resetContext(context);
expect(context.strokeStyle).to.be.an.instanceof(CanvasGradient);
});

it('attr(key, value), opacity', function() {
e.attr('opacity', 0.1);
expect(e.attr('opacity')).to.equal(0.1);
Expand Down Expand Up @@ -91,7 +119,9 @@ describe('Element', function() {
minX: 0,
maxX: 0,
minY: 0,
maxY: 0
maxY: 0,
height: 0,
width: 0
});
});

Expand Down

0 comments on commit 20b18a9

Please sign in to comment.