diff --git a/src/effects/texture.js b/src/effects/texture.js index ec7c0d10f..165dc1a60 100644 --- a/src/effects/texture.js +++ b/src/effects/texture.js @@ -13,7 +13,7 @@ anchor = document.createElement('a'); } - var Texture = Two.Texture = function(src, callback) { + var Texture = Two.Texture = function(source, callback) { this._renderer = {}; this._renderer.type = 'texture'; @@ -35,10 +35,10 @@ this.bind(Two.Events.load, loaded); } - if (_.isString(src)) { - this.src = src; - } else if (_.isElement(src)) { - this.image = src; + if (_.isString(source)) { + this.src = source; + } else if (_.isElement(source)) { + this.image = source; } this._update(); diff --git a/src/image.js b/src/image.js new file mode 100644 index 000000000..bdaa73763 --- /dev/null +++ b/src/image.js @@ -0,0 +1,113 @@ +(function(Two) { + +// output example: +// +// + + var _ = Two.Utils; + + /** + * @class + * @description Class that renders an image as an Image tag, it does not use the SVG header. + * @param {number} [x=0] - x position at center + * @param {numbert} [y=0] - y position at center + * @param {number} [width] - width of image + * @param {number} [height] - height of image + * @param {string} [imageDataSource] - image data encoded as a base 64 string + * @param {boolean} [preserveAspectRatio] - boolean to preserve the aspect ratio + */ + var Image = Two.Image = function(x, y, width, height, imageDataSource, preserveAspectRatio) { + + Two.Shape.call(this); + + this._renderer.type = 'image'; + + this.width = width; + this.height = height; + + this.href = imageDataSource; + this.opacity = 1.0; + this.className = ''; + this.visible = true; + + // this should better be in future an enum that can describe what all renders can do. + // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio + if (!_.isUndefined(preserveAspectRatio)) { + this.preserveAspectRatio = !!preserveAspectRatio; + } + + if (_.isNumber(x) && _.isNumber(y)) { + this.translation.set(x, y); + } + + this._update(); + + }; + + _.extend(Image, { + + Properties: [ + 'width', + 'height', + 'href', + 'preserveAspectRatio', + 'opacity', + 'className', + 'visible' + ], + + MakeObservable: function(obj) { + Two.Shape.MakeObservable(obj); + _.each(Image.Properties, Two.Utils.defineProperty, obj); + } + + }); + + _.extend(Image.prototype, Two.Shape.prototype, { + + _flagWidth: false, + _flagHeight: false, + _flagHref: false, + _flagPreserveAspectRatio: false, + _flagVisible: false, + _flagClassName: false, + _flagOpacity: false, + + _width: 0, + _height: 0, + _href: '', + _preserveAspectRatio: false, + _opacity: 1.0, + _className: '', + _visible: true, + + constructor: Image, + + _update: function() { + + Two.Shape.prototype._update.call(this); + return this; + + }, + + flagReset: function() { + + this._flagWidth = this._flagHeight = this._flagHref + = this._flagPreserveAspectRatio = this._flagVisible + = this._flagClassName = this._flagOpacity = false; + + Two.Shape.prototype.flagReset.call(this); + return this; + + } + + }); + + Image.MakeObservable(Image.prototype); + +})((typeof global !== 'undefined' ? global : (this || window)).Two); diff --git a/src/renderer/canvas.js b/src/renderer/canvas.js index 86d246633..e137a356b 100644 --- a/src/renderer/canvas.js +++ b/src/renderer/canvas.js @@ -614,6 +614,40 @@ return this.flagReset(); + }, + + image: function(ctx, forced) { + + var matrix = this._matrix.elements; + var width = this._width; + var height = this._height; + var href = this._href; + var preserveAspectRatio = this._preserveAspectRatio; + var opacity = this._opacity; + var visible = this._visible; + var defaultMatrix = isDefaultMatrix(matrix); + + if (!forced && (!visible)) { + return this; + } + + if (!defaultMatrix) { + ctx.save(); + ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); + } + + // TODO: Handle `preserveAspectRatio` intelligently... + var image = Two.Texture.getImage(href); + + if (image) { + ctx.save(); + ctx.translate(x, y); + ctx.drawImage(image, - width / 2, - height / 2, width, height); + ctx.restore(); + } + + return this.flagReset(); + } }, diff --git a/src/renderer/svg.js b/src/renderer/svg.js index 4bea64994..c229c31ee 100644 --- a/src/renderer/svg.js +++ b/src/renderer/svg.js @@ -883,8 +883,69 @@ } + }, + + image: { + + render: function(domElement) { + + this._update(); + + var changed = {}; + + var flagMatrix = this._matrix.manual || this._flagMatrix; + + if (flagMatrix) { + changed.transform = 'matrix(' + this._matrix.toString() + ')'; + } + + if (this._flagWidth) { + changed['width'] = this._width; + } + + if (this._flagHeight) { + changed['height'] = this._height; + } + + if (this._flagHref) { + changed['href'] = this._href; + } + + if (this._flagPreserveAspectRatio) { + changed['preserveAspectRatio'] = this._preserveAspectRatio ? 'none' : 'meet'; + } + + if (this._flagOpacity) { + changed.opacity = this._opacity; + } + + if (this._flagClassName) { + changed['class'] = this._className; + } + + if (this._flagVisible) { + changed.visibility = this._visible ? 'visible' : 'hidden'; + } + + if (!this._renderer.elem) { + + changed.id = this.id; + + this._renderer.elem = svg.createElement('image', changed); + domElement.defs.appendChild(this._renderer.elem); + + } else { + + svg.setAttributes(this._renderer.elem, changed); + + } + return this.flagReset(); + + } + } + }; /** diff --git a/utils/build.js b/utils/build.js index aa2ebbc17..2c805e211 100644 --- a/utils/build.js +++ b/utils/build.js @@ -20,6 +20,7 @@ var files = [ path.resolve(__dirname, '../src/renderer/webgl.js'), path.resolve(__dirname, '../src/shape.js'), path.resolve(__dirname, '../src/path.js'), + path.resolve(__dirname, '../src/image.js'), path.resolve(__dirname, '../src/shapes/line.js'), path.resolve(__dirname, '../src/shapes/rectangle.js'), path.resolve(__dirname, '../src/shapes/ellipse.js'),