mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-24 05:03:44 +00:00
Merge remote-tracking branch 'upstream/master'
Conflicts: .gitignore dist/fabric.js dist/fabric.require.js src/brushes/circle_brush.class.js src/canvas.class.js src/mixins/canvas_events.mixin.js src/mixins/object_geometry.mixin.js src/mixins/object_interactivity.mixin.js src/shapes/group.class.js src/shapes/image.class.js src/shapes/object.class.js src/shapes/path_group.class.js src/shapes/text.class.js
This commit is contained in:
commit
0d8db88e71
111 changed files with 7883 additions and 4866 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
before_commit
|
||||
dist/fabric.js
|
||||
37
.jscs.json
Normal file
37
.jscs.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"requireCurlyBraces": [ "if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"requireSpaceBeforeBinaryOperators": ["+", "-", "*", "=", "==", "===", "!=", "!=="],
|
||||
"requireSpaceAfterBinaryOperators": ["+", "-", "*", "=", "==", "===", "!=", "!=="],
|
||||
|
||||
"requireParenthesesAroundIIFE": true,
|
||||
"requireSpacesInsideObjectBrackets": "all",
|
||||
"requireCommaBeforeLineBreak": true,
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","],
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
||||
"requireKeywordsOnNewLine": ["else"],
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireDotNotation": true,
|
||||
"requireMultipleVarDecl": true,
|
||||
|
||||
"disallowEmptyBlocks": true,
|
||||
"disallowQuotedKeysInObjects": "allButReserved",
|
||||
"disallowSpaceAfterObjectKeys": true,
|
||||
|
||||
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
|
||||
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--"],
|
||||
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineStrings": true,
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
|
||||
"validateLineBreaks": "LF",
|
||||
"validateQuoteMarks": "'",
|
||||
"validateIndentation": 2,
|
||||
|
||||
"safeContextKeyword": "_this"
|
||||
}
|
||||
30
.jshintrc
30
.jshintrc
|
|
@ -1,49 +1,41 @@
|
|||
{
|
||||
"globals": {
|
||||
"ActiveXObject": true,
|
||||
"Cufon": true,
|
||||
"define": true,
|
||||
"Event": true,
|
||||
"exports": true,
|
||||
"fabric": true,
|
||||
"Cufon": true,
|
||||
"Event": true,
|
||||
"G_vmlCanvasManager": true,
|
||||
"ActiveXObject": true
|
||||
"G_vmlCanvasManager": true
|
||||
},
|
||||
|
||||
"node": true,
|
||||
"es5": false,
|
||||
"browser": true,
|
||||
|
||||
"boss": false,
|
||||
"curly": false,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"eqnull": true,
|
||||
"evil": true,
|
||||
"expr": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"lastsemic": true,
|
||||
"laxbreak": true,
|
||||
"loopfunc": true,
|
||||
"multistr": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"node": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"nonew": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"sub": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"lastsemic": true,
|
||||
|
||||
// "maxparams": 4
|
||||
// "maxcomplexity": 7
|
||||
// "maxlen": 100
|
||||
"maxdepth": 4,
|
||||
// "maxlen": 100
|
||||
// "maxparams": 4
|
||||
"maxstatements": 30
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ src/
|
|||
lib/
|
||||
dist/all.min.js
|
||||
dist/all.min.js.gz
|
||||
dist/fabric.min.js
|
||||
dist/fabric.min.js.gz
|
||||
.DS_Store
|
||||
HEADER.js
|
||||
build.js
|
||||
build.js
|
||||
|
|
|
|||
13
.sublime-project
vendored
13
.sublime-project
vendored
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": ".",
|
||||
"folder_exclude_patterns": ["tmp", "log", "node_modules", "docs", "jsdoc-toolkit"],
|
||||
"file_exclude_patterns": ["*.min.js", "*.require.js", "*.gz", "*.sublime-workspace"]
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"tab_size": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
- 0.8
|
||||
- 0.10
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
script: 'npm run-script build && npm test'
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libgif-dev libpng-dev libjpeg8-dev libpango1.0-dev libcairo2-dev
|
||||
- '[ "${TRAVIS_NODE_VERSION}" = "0.6" ] && npm conf set strict-ssl false || true'
|
||||
|
|
|
|||
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,5 +1,22 @@
|
|||
**Edge**
|
||||
|
||||
- [BACK_INCOMPAT] `fabric.Collection#remove` doesn't return removed object -> returns `this` (chainable)
|
||||
|
||||
- Add "mouse:over" and "mouse:out" canvas events (and corresponding "mouseover", "mouseout" object events)
|
||||
- Add support for passing options to `fabric.createCanvasForNode`
|
||||
|
||||
- Various iText fixes and performance improvements
|
||||
- Fix `overlayImage` / `overlayColor` during selection mode
|
||||
- Fix double callback in loadFromJSON when there's no objects
|
||||
- Fix paths parsing when number has negative exponent
|
||||
- Fix background offset in iText
|
||||
- Fix style object deletion in iText
|
||||
- Fix typo in `_initCanvasHandlers`
|
||||
- Fix `transformMatrix` not affecting fabric.Text
|
||||
- Fix `setAngle` for different originX/originY (!= 'center')
|
||||
- Change default/init noise/brightness value for `fabric.Image.filters.Noise` and `fabric.Image.filters.Brightness` from 100 to 0
|
||||
- Add `fabric.Canvas#imageSmoothingEnabled`
|
||||
|
||||
**Version 1.4.0**
|
||||
|
||||
- [BACK_INCOMPAT] JSON and Cufon are no longer included in default build
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ If you are sure that it's a bug in Fabric.js or a suggestion, open a new [issue]
|
|||
perfect, but even a simple script demonstrating the error would suffice. You could use [this jsfiddle template](http://jsfiddle.net/fabricjs/Da7SP/) as a
|
||||
starting point.
|
||||
|
||||
- **Fabric.js version:** Make sure to specify which version of Fabric.js you are using. The version can be found in [all.js file](https://github.com/kangax/fabric.js/blob/master/dist/all.js#L5) or just by executing `fabric.version` in the browser console.
|
||||
- **Fabric.js version:** Make sure to specify which version of Fabric.js you are using. The version can be found in [fabric.js file](https://github.com/kangax/fabric.js/blob/master/dist/fabric.js#L5) or just by executing `fabric.version` in the browser console.
|
||||
|
||||
## Pull requests
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */
|
||||
/*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
|
||||
|
||||
var fabric = fabric || { version: "1.4.0" };
|
||||
var fabric = fabric || { version: "1.4.6" };
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.fabric = fabric;
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
|
|||
* @type array
|
||||
*/
|
||||
fabric.SHARED_ATTRIBUTES = [
|
||||
"display",
|
||||
"transform",
|
||||
"fill", "fill-opacity", "fill-rule",
|
||||
"opacity",
|
||||
|
|
|
|||
4
LICENSE
4
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2008-2013 Printio (Juriy Zaytsev, Maxim Chernyak)
|
||||
Copyright (c) 2008-2014 Printio (Juriy Zaytsev, Maxim Chernyak)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -13,4 +13,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
|
|
|||
75
README.md
75
README.md
|
|
@ -1,12 +1,15 @@
|
|||
<a href="http://fabricjs.challengepost.com/?utm_source=partner&utm_medium=banner&utm_campaign=fabricjs" style="display: block">
|
||||
<img src="https://dl.dropboxusercontent.com/u/822184/fabric-js-promo-widget-github.gif" style="width: auto">
|
||||
</a>
|
||||
|
||||
### Fabric
|
||||
|
||||
[](http://travis-ci.org/#!/kangax/fabric.js)
|
||||
[](https://codeclimate.com/repos/526a0ed089af7e6cf2001389/feed)
|
||||
[](https://coveralls.io/r/kangax/fabric.js?branch=master)
|
||||
<a href="https://npmjs.org/package/fabric"><img src="https://badge.fury.io/js/fabric.png"></a>
|
||||
[](http://badge.fury.io/js/fabric)
|
||||
[](http://badge.fury.io/bo/fabric)
|
||||
|
||||
|
||||
[](https://david-dm.org/kangax/fabric.js)
|
||||
[](https://david-dm.org/kangax/fabric.js#info=devDependencies)
|
||||
|
||||
|
||||
**Fabric.js** is a framework that makes it easy to work with HTML5 canvas element. It is an **interactive object model** on top of canvas element. It is also an **SVG-to-canvas parser**.
|
||||
|
||||
|
|
@ -18,7 +21,7 @@ Using Fabric.js, you can create and populate objects on canvas; objects like sim
|
|||
|
||||
### Goals
|
||||
|
||||
- Unit tested (2300+ tests at the moment)
|
||||
- Unit tested (2400+ tests at the moment)
|
||||
- Modular (~60 small ["classes", modules, mixins](http://fabricjs.com/docs/))
|
||||
- Cross-browser
|
||||
- [Fast](https://github.com/kangax/fabric.js/wiki/Focus-on-speed)
|
||||
|
|
@ -104,7 +107,15 @@ Fabric.js started as a foundation for design editor on [printio.ru](http://print
|
|||
|
||||
If you use google closure compiler you have to add `sourceMappingURL` manually at the end of the minified file all.min.js (see issue https://code.google.com/p/closure-compiler/issues/detail?id=941).
|
||||
|
||||
//# sourceMappingURL=all.min.js.map
|
||||
//# sourceMappingURL=fabric.min.js.map
|
||||
|
||||
6. Lint source code (prerequisite: `npm -g install jshint`)
|
||||
|
||||
$ jshint src
|
||||
|
||||
7. Ensure code guidelines are met (prerequisite: `npm -g install jscs`)
|
||||
|
||||
$ jscs src
|
||||
|
||||
### Demos
|
||||
|
||||
|
|
@ -112,6 +123,8 @@ Fabric.js started as a foundation for design editor on [printio.ru](http://print
|
|||
- [Kitchensink demo](http://fabricjs.com/kitchensink/)
|
||||
- [Benchmarks](http://fabricjs.com/benchmarks/)
|
||||
|
||||
[Who's using Fabric?](http://trends.builtwith.com/javascript/FabricJS)
|
||||
|
||||
### Documentation
|
||||
|
||||
Documentation is always available at [http://fabricjs.com/docs/](http://fabricjs.com/docs/).
|
||||
|
|
@ -137,11 +150,11 @@ These are the optional modules that could be specified for inclusion, when build
|
|||
|
||||
Additional flags for build script are:
|
||||
|
||||
- **requirejs** — Makes fabric requirejs AMD-compatible in `dist/all.js`. *Note:* an unminified, requirejs-compatible version is always created in `dist/all.require.js`
|
||||
- **requirejs** — Makes fabric requirejs AMD-compatible in `dist/fabric.js`. *Note:* an unminified, requirejs-compatible version is always created in `dist/fabric.require.js`
|
||||
- **no-strict** — Strips "use strict" directives from source
|
||||
- **no-svg-export** — Removes svg exporting functionality
|
||||
- **no-es5-compat** - Removes ES5 compat methods (Array.prototype.*, String.prototype.*, Function.prototype.*)
|
||||
- **sourcemap** - Generates a sourceMap file and adds the `sourceMappingURL` (only if uglifyjs is used) to `dist/all.min.js`
|
||||
- **sourcemap** - Generates a sourceMap file and adds the `sourceMappingURL` (only if uglifyjs is used) to `dist/fabric.min.js`
|
||||
|
||||
For example:
|
||||
|
||||
|
|
@ -151,19 +164,36 @@ For example:
|
|||
|
||||
#### Adding red rectangle to canvas
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" width="300" height="300"></canvas>
|
||||
...
|
||||
var canvas = new fabric.Canvas('canvas');
|
||||
|
||||
var rect = new fabric.Rect({
|
||||
top: 100,
|
||||
left: 100,
|
||||
width: 60,
|
||||
height: 70,
|
||||
fill: 'red'
|
||||
});
|
||||
<script src="lib/fabric.js"></script>
|
||||
<script>
|
||||
var canvas = new fabric.Canvas('canvas');
|
||||
|
||||
canvas.add(rect);
|
||||
var rect = new fabric.Rect({
|
||||
top : 100,
|
||||
left : 100,
|
||||
width : 60,
|
||||
height : 70,
|
||||
fill : 'red'
|
||||
});
|
||||
|
||||
canvas.add(rect);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Helping Fabric
|
||||
|
||||
- [Fabric on Bountysource](https://www.bountysource.com/trackers/23217-fabric-js)
|
||||
- [Fabric on CodeTriage](http://www.codetriage.com/kangax/fabric.js)
|
||||
|
||||
### Staying in touch
|
||||
|
||||
|
|
@ -175,6 +205,8 @@ See [Fabric questions on Stackoverflow](stackoverflow.com/questions/tagged/fabri
|
|||
Fabric snippets on [jsfiddle](http://jsfiddle.net/user/fabricjs/fiddles/)
|
||||
or [codepen.io](http://codepen.io/tag/fabricjs).
|
||||
|
||||
Fabric on [LibKnot](http://libknot.ohmztech.com/).
|
||||
|
||||
Get help in Fabric's IRC channel — irc://irc.freenode.net/#fabric.js
|
||||
|
||||
### Credits
|
||||
|
|
@ -187,7 +219,7 @@ Get help in Fabric's IRC channel — irc://irc.freenode.net/#fabric.js
|
|||
|
||||
### MIT License
|
||||
|
||||
Copyright (c) 2008-2013 Printio (Juriy Zaytsev, Maxim Chernyak)
|
||||
Copyright (c) 2008-2014 Printio (Juriy Zaytsev, Maxim Chernyak)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -203,3 +235,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
|
|
|||
28
bower.json
Normal file
28
bower.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "fabric.js",
|
||||
"version": "1.4.6",
|
||||
"homepage": "http://fabricjs.com",
|
||||
"authors": [
|
||||
"kangax", "Kienz"
|
||||
],
|
||||
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
||||
"main": "./dist/fabric.min.js",
|
||||
"ignore": [
|
||||
"lib",
|
||||
"src",
|
||||
"test",
|
||||
"*.*",
|
||||
".*"
|
||||
],
|
||||
"keywords": [
|
||||
"canvas",
|
||||
"graphic",
|
||||
"graphics",
|
||||
"SVG",
|
||||
"node-canvas",
|
||||
"parser",
|
||||
"HTML5",
|
||||
"object model"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
37
build.js
37
build.js
|
|
@ -3,8 +3,7 @@ var fs = require('fs'),
|
|||
|
||||
var buildArgs = process.argv.slice(2),
|
||||
buildArgsAsObject = { },
|
||||
rootPath = process.cwd(),
|
||||
distributionPath = 'dist/';
|
||||
rootPath = process.cwd();
|
||||
|
||||
buildArgs.forEach(function(arg) {
|
||||
var key = arg.split('=')[0],
|
||||
|
|
@ -16,6 +15,7 @@ buildArgs.forEach(function(arg) {
|
|||
var modulesToInclude = buildArgsAsObject.modules ? buildArgsAsObject.modules.split(',') : [ ];
|
||||
var modulesToExclude = buildArgsAsObject.exclude ? buildArgsAsObject.exclude.split(',') : [ ];
|
||||
|
||||
var distributionPath = buildArgsAsObject.dest || 'dist/';
|
||||
var minifier = buildArgsAsObject.minifier || 'uglifyjs';
|
||||
var mininfierCmd;
|
||||
|
||||
|
|
@ -43,17 +43,17 @@ if (sourceMap) {
|
|||
console.log('[notice]: sourceMap support requires uglifyjs or google closure compiler as minifier; changed minifier to uglifyjs.');
|
||||
minifier = 'uglifyjs';
|
||||
}
|
||||
sourceMapFlags = minifier === 'uglifyjs' ? ' --source-map all.min.js.map' : ' --create_source_map all.min.js.map --source_map_format=V3';
|
||||
sourceMapFlags = minifier === 'uglifyjs' ? ' --source-map fabric.min.js.map' : ' --create_source_map fabric.min.js.map --source_map_format=V3';
|
||||
}
|
||||
|
||||
if (minifier === 'yui') {
|
||||
mininfierCmd = 'java -jar ' + rootPath + '/lib/yuicompressor-2.4.6.jar all.js -o all.min.js';
|
||||
mininfierCmd = 'java -jar ' + rootPath + '/lib/yuicompressor-2.4.6.jar fabric.js -o fabric.min.js';
|
||||
}
|
||||
else if (minifier === 'closure') {
|
||||
mininfierCmd = 'java -jar ' + rootPath + '/lib/google_closure_compiler.jar --js all.js --js_output_file all.min.js' + sourceMapFlags;
|
||||
mininfierCmd = 'java -jar ' + rootPath + '/lib/google_closure_compiler.jar --js fabric.js --js_output_file fabric.min.js' + sourceMapFlags;
|
||||
}
|
||||
else if (minifier === 'uglifyjs') {
|
||||
mininfierCmd = 'uglifyjs ' + amdUglifyFlags + ' --output all.min.js all.js' + sourceMapFlags;
|
||||
mininfierCmd = 'uglifyjs ' + amdUglifyFlags + ' --output fabric.min.js fabric.js' + sourceMapFlags;
|
||||
}
|
||||
|
||||
var buildSh = 'build-sh' in buildArgsAsObject;
|
||||
|
|
@ -233,6 +233,7 @@ var filesToInclude = [
|
|||
ifSpecifiedInclude('image_filters', 'src/filters/sepia_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/sepia2_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/tint_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/multiply_filter.class.js'),
|
||||
|
||||
ifSpecifiedInclude('text', 'src/shapes/text.class.js'),
|
||||
ifSpecifiedInclude('cufon', 'src/shapes/text.cufon.js'),
|
||||
|
|
@ -285,7 +286,7 @@ else {
|
|||
process.chdir(distributionPath);
|
||||
|
||||
appendFileContents(filesToInclude, function() {
|
||||
fs.writeFile('all.js', distFileContents, function (err) {
|
||||
fs.writeFile('fabric.js', distFileContents, function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
|
|
@ -293,13 +294,13 @@ else {
|
|||
|
||||
// add js wrapping in AMD closure for requirejs if necessary
|
||||
if (amdLib !== false) {
|
||||
exec('uglifyjs all.js ' + amdUglifyFlags + ' -b --output all.js');
|
||||
exec('uglifyjs fabric.js ' + amdUglifyFlags + ' -b --output fabric.js');
|
||||
}
|
||||
|
||||
if (amdLib !== false) {
|
||||
console.log('Built distribution to ' + distributionPath + 'all.js (' + amdLib + '-compatible)');
|
||||
console.log('Built distribution to ' + distributionPath + 'fabric.js (' + amdLib + '-compatible)');
|
||||
} else {
|
||||
console.log('Built distribution to ' + distributionPath + 'all.js');
|
||||
console.log('Built distribution to ' + distributionPath + 'fabric.js');
|
||||
}
|
||||
|
||||
exec(mininfierCmd, function (error, output) {
|
||||
|
|
@ -307,18 +308,18 @@ else {
|
|||
console.error('Minification failed using', minifier, 'with', mininfierCmd);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('Minified using', minifier, 'to ' + distributionPath + 'all.min.js');
|
||||
console.log('Minified using', minifier, 'to ' + distributionPath + 'fabric.min.js');
|
||||
|
||||
if (sourceMapFlags) {
|
||||
console.log('Built sourceMap to ' + distributionPath + 'all.min.js.map');
|
||||
console.log('Built sourceMap to ' + distributionPath + 'fabric.min.js.map');
|
||||
}
|
||||
|
||||
exec('gzip -c all.min.js > all.min.js.gz', function (error, output) {
|
||||
console.log('Gzipped to ' + distributionPath + 'all.min.js.gz');
|
||||
exec('gzip -c fabric.min.js > fabric.min.js.gz', function (error, output) {
|
||||
console.log('Gzipped to ' + distributionPath + 'fabric.min.js.gz');
|
||||
});
|
||||
});
|
||||
|
||||
// Always build requirejs AMD module in all.require.js
|
||||
// Always build requirejs AMD module in fabric.require.js
|
||||
// add necessary requirejs footer code to filesToInclude if we haven't before
|
||||
if (amdLib === false) {
|
||||
amdLib = "requirejs";
|
||||
|
|
@ -326,13 +327,13 @@ else {
|
|||
}
|
||||
|
||||
appendFileContents(filesToInclude, function() {
|
||||
fs.writeFile('all.require.js', distFileContents, function (err) {
|
||||
fs.writeFile('fabric.require.js', distFileContents, function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
exec('uglifyjs all.require.js ' + amdUglifyFlags + ' -b --output all.require.js');
|
||||
console.log('Built distribution to ' + distributionPath + 'all.require.js (requirejs-compatible)');
|
||||
exec('uglifyjs fabric.require.js ' + amdUglifyFlags + ' -b --output fabric.require.js');
|
||||
console.log('Built distribution to ' + distributionPath + 'fabric.require.js (requirejs-compatible)');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "fabric.js",
|
||||
"repo": "kangax/fabric.js",
|
||||
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
||||
"version": "1.4.0",
|
||||
"keywords": ["canvas", "graphic", "graphics", "SVG", "node-canvas", "parser", "HTML5", "object model"],
|
||||
"dependencies": {},
|
||||
"development": {},
|
||||
"license": "MIT",
|
||||
"scripts": [
|
||||
"./dist/all.js"
|
||||
]
|
||||
}
|
||||
7
dist/all.min.js
vendored
7
dist/all.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/all.min.js.gz
vendored
BIN
dist/all.min.js.gz
vendored
Binary file not shown.
3725
dist/all.require.js → dist/fabric.js
vendored
3725
dist/all.require.js → dist/fabric.js
vendored
File diff suppressed because it is too large
Load diff
7
dist/fabric.min.js
vendored
Normal file
7
dist/fabric.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
dist/fabric.min.js.gz
vendored
Normal file
BIN
dist/fabric.min.js.gz
vendored
Normal file
Binary file not shown.
3727
dist/all.js → dist/fabric.require.js
vendored
3727
dist/all.js → dist/fabric.require.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Should objects by aligned by a bounding box?
|
||||
* Should objects be aligned by a bounding box?
|
||||
* [Bug] Scaled objects sometimes can not be aligned by edges
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ if (typeof(eventjs) === "undefined") var eventjs = Event;
|
|||
(function(root) { "use strict";
|
||||
|
||||
// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness).
|
||||
root.modifyEventListener = true;
|
||||
root.modifyEventListener = false;
|
||||
|
||||
// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness).
|
||||
root.modifySelectors = true;
|
||||
root.modifySelectors = false;
|
||||
|
||||
// Event maintenance.
|
||||
root.add = function(target, type, listener, configure) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Index: excanvas.js
|
|||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
o2.lineScale_ = o1.lineScale_;
|
||||
+ o2.rotation_ = o1.rotation_; // used for images
|
||||
+ o2.rotation_ = o1.rotation_; // used for images
|
||||
}
|
||||
|
||||
var colorData = {
|
||||
|
|
@ -14,7 +14,7 @@ Index: excanvas.js
|
|||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
this.lineScale_ = 1;
|
||||
+ this.rotation_ = 0;
|
||||
+ this.rotation_ = 0;
|
||||
}
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
|
|
@ -26,10 +26,10 @@ Index: excanvas.js
|
|||
+ contextPrototype.drawImage = function(image) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
-
|
||||
+
|
||||
+
|
||||
+ // to fix new Image() we check the existance of runtimeStyle
|
||||
+ var rts = image.runtimeStyle.width;
|
||||
+
|
||||
+
|
||||
// to find the original width we overide the width and height
|
||||
- var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
- var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
|
|
@ -38,25 +38,25 @@ Index: excanvas.js
|
|||
+ if(rts) {
|
||||
+ var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
+ var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
+
|
||||
+
|
||||
+ image.runtimeStyle.width = 'auto';
|
||||
+ image.runtimeStyle.height = 'auto';
|
||||
+ image.runtimeStyle.height = 'auto';
|
||||
+ }
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
-
|
||||
+
|
||||
+
|
||||
// and remove overides
|
||||
- image.runtimeStyle.width = oldRuntimeWidth;
|
||||
- image.runtimeStyle.height = oldRuntimeHeight;
|
||||
-
|
||||
+ if(rts) {
|
||||
+ image.runtimeStyle.width = oldRuntimeWidth;
|
||||
+ image.runtimeStyle.height = oldRuntimeHeight;
|
||||
+ image.runtimeStyle.height = oldRuntimeHeight;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
|
|
@ -64,9 +64,9 @@ Index: excanvas.js
|
|||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
+
|
||||
+
|
||||
+ var scaleX = scaleY = 1;
|
||||
+
|
||||
+
|
||||
+ // FIX: divs give better quality then vml image and also fixes transparent PNG's
|
||||
+ vmlStr.push(' <div style="position:absolute;');
|
||||
|
||||
|
|
@ -94,21 +94,21 @@ Index: excanvas.js
|
|||
- 'Dy=', mr(d.y / Z), '');
|
||||
+ // Scaling images using width & height instead of Transform Matrix
|
||||
+ // because of quality loss
|
||||
+ var c = mc(this.rotation_);
|
||||
+ var c = mc(this.rotation_);
|
||||
+ var s = ms(this.rotation_);
|
||||
+
|
||||
+
|
||||
+ // Inverse rotation matrix
|
||||
+ var irm = [
|
||||
+ [c, -s, 0],
|
||||
+ [s, c, 0],
|
||||
+ [c, -s, 0],
|
||||
+ [s, c, 0],
|
||||
+ [0, 0, 1]
|
||||
+ ];
|
||||
+
|
||||
+ ];
|
||||
+
|
||||
+ // Get unrotated matrix to get only scaling values
|
||||
+ var urm = matrixMultiply(irm, this.m_);
|
||||
+ var urm = matrixMultiply(irm, this.m_);
|
||||
+ scaleX = urm[0][0];
|
||||
+ scaleY = urm[1][1];
|
||||
+
|
||||
+
|
||||
+ // Apply only rotation and translation to Matrix
|
||||
+ filter.push('M11=', c, ',',
|
||||
+ 'M12=', -s, ',',
|
||||
|
|
@ -149,25 +149,25 @@ Index: excanvas.js
|
|||
+ ' filter:progid:DxImageTransform.Microsoft.Matrix(Dx=',
|
||||
+ -sx * dw / sw * scaleX, ',Dy=', -sy * dh / sh * scaleY, ');">');
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+
|
||||
+
|
||||
+ // Apply scales to width and height
|
||||
+ vmlStr.push('<div style="width:', Math.round(scaleX * w * dw / sw), 'px;',
|
||||
+ ' height:', Math.round(scaleY * h * dh / sh), 'px;',
|
||||
+ ' filter:');
|
||||
+
|
||||
+
|
||||
+ // If there is a globalAlpha, apply it to image
|
||||
+ if(this.globalAlpha < 1) {
|
||||
+ vmlStr.push(' progid:DXImageTransform.Microsoft.Alpha(opacity=' + (this.globalAlpha * 100) + ')');
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ vmlStr.push(' progid:DXImageTransform.Microsoft.AlphaImageLoader(src=', image.src, ',sizingMethod=scale)">');
|
||||
+
|
||||
+ // Close the crop div if necessary
|
||||
+
|
||||
+ // Close the crop div if necessary
|
||||
+ if (sx || sy) vmlStr.push('</div>');
|
||||
+
|
||||
+
|
||||
+ vmlStr.push('</div></div>');
|
||||
+
|
||||
+
|
||||
+ this.element_.insertAdjacentHTML('beforeEnd', vmlStr.join(''));
|
||||
};
|
||||
|
||||
|
|
@ -176,8 +176,8 @@ Index: excanvas.js
|
|||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
+ this.rotation_ += aRot;
|
||||
+
|
||||
+ this.rotation_ += aRot;
|
||||
+
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
|
|
@ -188,7 +188,7 @@ Index: excanvas.js
|
|||
- this.textMeasureEl_.style.font = this.font;
|
||||
+ // FIX: Apply current font style to textMeasureEl to get correct size
|
||||
+ var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_),
|
||||
+ fontStyleString = buildStyle(fontStyle);
|
||||
+ fontStyleString = buildStyle(fontStyle);
|
||||
+ this.textMeasureEl_.style.font = fontStyleString;
|
||||
+
|
||||
// Don't use innerHTML or innerText because they allow markup/whitespace.
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ if (!document.createElement('canvas').getContext) {
|
|||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
o2.lineScale_ = o1.lineScale_;
|
||||
o2.rotation_ = o1.rotation_; // used for images
|
||||
o2.rotation_ = o1.rotation_; // used for images
|
||||
}
|
||||
|
||||
var colorData = {
|
||||
|
|
@ -604,7 +604,7 @@ if (!document.createElement('canvas').getContext) {
|
|||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
this.lineScale_ = 1;
|
||||
this.rotation_ = 0;
|
||||
this.rotation_ = 0;
|
||||
}
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
|
|
@ -771,29 +771,29 @@ if (!document.createElement('canvas').getContext) {
|
|||
|
||||
contextPrototype.drawImage = function(image) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
|
||||
|
||||
// to fix new Image() we check the existance of runtimeStyle
|
||||
var rts = image.runtimeStyle.width;
|
||||
|
||||
|
||||
// to find the original width we overide the width and height
|
||||
if(rts) {
|
||||
var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
|
||||
|
||||
image.runtimeStyle.width = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
}
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
|
||||
|
||||
// and remove overides
|
||||
if(rts) {
|
||||
image.runtimeStyle.width = oldRuntimeWidth;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
}
|
||||
|
||||
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
|
|
@ -830,9 +830,9 @@ if (!document.createElement('canvas').getContext) {
|
|||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
|
||||
var scaleX = scaleY = 1;
|
||||
|
||||
|
||||
// FIX: divs give better quality then vml image and also fixes transparent PNG's
|
||||
vmlStr.push(' <div style="position:absolute;');
|
||||
|
||||
|
|
@ -846,21 +846,21 @@ if (!document.createElement('canvas').getContext) {
|
|||
|
||||
// Scaling images using width & height instead of Transform Matrix
|
||||
// because of quality loss
|
||||
var c = mc(this.rotation_);
|
||||
var c = mc(this.rotation_);
|
||||
var s = ms(this.rotation_);
|
||||
|
||||
|
||||
// Inverse rotation matrix
|
||||
var irm = [
|
||||
[c, -s, 0],
|
||||
[s, c, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
];
|
||||
|
||||
// Get unrotated matrix to get only scaling values
|
||||
var urm = matrixMultiply(irm, this.m_);
|
||||
var urm = matrixMultiply(irm, this.m_);
|
||||
scaleX = urm[0][0];
|
||||
scaleY = urm[1][1];
|
||||
|
||||
|
||||
// Apply only rotation and translation to Matrix
|
||||
filter.push('M11=', c, ',',
|
||||
'M12=', -s, ',',
|
||||
|
|
@ -896,25 +896,25 @@ if (!document.createElement('canvas').getContext) {
|
|||
' filter:progid:DxImageTransform.Microsoft.Matrix(Dx=',
|
||||
-sx * dw / sw * scaleX, ',Dy=', -sy * dh / sh * scaleY, ');">');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Apply scales to width and height
|
||||
vmlStr.push('<div style="width:', Math.round(scaleX * w * dw / sw), 'px;',
|
||||
' height:', Math.round(scaleY * h * dh / sh), 'px;',
|
||||
' filter:');
|
||||
|
||||
|
||||
// If there is a globalAlpha, apply it to image
|
||||
if(this.globalAlpha < 1) {
|
||||
vmlStr.push(' progid:DXImageTransform.Microsoft.Alpha(opacity=' + (this.globalAlpha * 100) + ')');
|
||||
}
|
||||
|
||||
|
||||
vmlStr.push(' progid:DXImageTransform.Microsoft.AlphaImageLoader(src=', image.src, ',sizingMethod=scale)">');
|
||||
|
||||
// Close the crop div if necessary
|
||||
|
||||
// Close the crop div if necessary
|
||||
if (sx || sy) vmlStr.push('</div>');
|
||||
|
||||
|
||||
vmlStr.push('</div></div>');
|
||||
|
||||
|
||||
this.element_.insertAdjacentHTML('beforeEnd', vmlStr.join(''));
|
||||
};
|
||||
|
||||
|
|
@ -1198,8 +1198,8 @@ if (!document.createElement('canvas').getContext) {
|
|||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
this.rotation_ += aRot;
|
||||
|
||||
this.rotation_ += aRot;
|
||||
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
|
|
@ -1355,7 +1355,7 @@ if (!document.createElement('canvas').getContext) {
|
|||
this.textMeasureEl_.innerHTML = '';
|
||||
// FIX: Apply current font style to textMeasureEl to get correct size
|
||||
var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_),
|
||||
fontStyleString = buildStyle(fontStyle);
|
||||
fontStyleString = buildStyle(fontStyle);
|
||||
this.textMeasureEl_.style.font = fontStyleString;
|
||||
|
||||
// Don't use innerHTML or innerText because they allow markup/whitespace.
|
||||
|
|
|
|||
40
lib/json2.js
40
lib/json2.js
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
json2.js
|
||||
2011-10-19
|
||||
2014-02-04
|
||||
|
||||
Public Domain.
|
||||
|
||||
|
|
@ -159,8 +159,7 @@
|
|||
// Create a JSON object only if one does not already exist. We create the
|
||||
// methods in a closure to avoid creating global variables.
|
||||
|
||||
var JSON;
|
||||
if (!JSON) {
|
||||
if (typeof JSON !== 'object') {
|
||||
JSON = {};
|
||||
}
|
||||
|
||||
|
|
@ -174,8 +173,7 @@ if (!JSON) {
|
|||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
/** @ignore */
|
||||
Date.prototype.toJSON = function (key) {
|
||||
Date.prototype.toJSON = function () {
|
||||
|
||||
return isFinite(this.valueOf())
|
||||
? this.getUTCFullYear() + '-' +
|
||||
|
|
@ -189,25 +187,16 @@ if (!JSON) {
|
|||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
/** @ignore */
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
Boolean.prototype.toJSON = function () {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
var cx,
|
||||
escapable,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
meta,
|
||||
rep;
|
||||
|
||||
|
||||
|
|
@ -359,7 +348,16 @@ if (!JSON) {
|
|||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
/** @ignore */
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
|
|
@ -407,7 +405,7 @@ if (!JSON) {
|
|||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
/** @ignore */
|
||||
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
|
|
@ -488,4 +486,4 @@ if (!JSON) {
|
|||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
}());
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 20 KiB |
44
package.json
44
package.json
|
|
@ -1,30 +1,46 @@
|
|||
{
|
||||
"name": "fabric",
|
||||
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
||||
"version": "1.4.0",
|
||||
"homepage": "http://fabricjs.com/",
|
||||
"version": "1.4.6",
|
||||
"author": "Juriy Zaytsev <kangax@gmail.com>",
|
||||
"keywords": ["canvas", "graphic", "graphics", "SVG", "node-canvas", "parser", "HTML5", "object model"],
|
||||
"repository": "git://github.com/kangax/fabric.js",
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/kangax/fabric.js/raw/master/LICENSE"
|
||||
}],
|
||||
"keywords": [
|
||||
"canvas",
|
||||
"graphic",
|
||||
"graphics",
|
||||
"SVG",
|
||||
"node-canvas",
|
||||
"parser",
|
||||
"HTML5",
|
||||
"object model"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kangax/fabric.js"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/kangax/fabric.js/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "node build.js modules=ALL exclude=json,cufon,gestures",
|
||||
"test": "node test.js && jshint src"
|
||||
},
|
||||
"dependencies": {
|
||||
"canvas": "1.0.x",
|
||||
"jsdom": "0.7.x",
|
||||
"jsdom": "0.10.x",
|
||||
"xmldom": "0.1.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"qunit": "0.5.x",
|
||||
"jshint": "2.3.x",
|
||||
"uglify-js": "2.4.x",
|
||||
"execSync": "0.0.x",
|
||||
"plato": "0.6.x"
|
||||
"uglify-js": "2.4.x",
|
||||
"jscs": "1.4.x",
|
||||
"jshint": "2.5.x",
|
||||
"qunit": "0.6.x",
|
||||
"istanbul": "0.2.6"
|
||||
},
|
||||
"engines": { "node": ">=0.4.0 && <1.0.0" },
|
||||
"main": "./dist/all.js"
|
||||
"engines": {
|
||||
"node": ">=0.4.0 && <1.0.0"
|
||||
},
|
||||
"main": "./dist/fabric.js"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ window.fabric = fabric;
|
|||
var exports = exports || {};
|
||||
exports.fabric = fabric;
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([], function() { return fabric });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|||
* @param {Object} pointer
|
||||
*/
|
||||
drawDot: function(pointer) {
|
||||
var point = this.addPoint(pointer);
|
||||
var ctx = this.canvas.contextTop;
|
||||
var point = this.addPoint(pointer),
|
||||
ctx = this.canvas.contextTop;
|
||||
|
||||
var v = this.canvas.viewportTransform;
|
||||
ctx.save();
|
||||
|
|
@ -69,15 +69,15 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|||
var circles = [ ];
|
||||
|
||||
for (var i = 0, len = this.points.length; i < len; i++) {
|
||||
var point = this.points[i];
|
||||
var circle = new fabric.Circle({
|
||||
radius: this.points[i].radius,
|
||||
left: point.x,
|
||||
top: point.y,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
fill: this.points[i].fill
|
||||
});
|
||||
var point = this.points[i],
|
||||
circle = new fabric.Circle({
|
||||
radius: point.radius,
|
||||
left: point.x,
|
||||
top: point.y,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
fill: point.fill
|
||||
});
|
||||
|
||||
this.shadow && circle.setShadow(this.shadow);
|
||||
|
||||
|
|
@ -100,12 +100,12 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|||
* @return {fabric.Point} Just added pointer point
|
||||
*/
|
||||
addPoint: function(pointer) {
|
||||
var pointerPoint = new fabric.Point(pointer.x, pointer.y);
|
||||
var pointerPoint = new fabric.Point(pointer.x, pointer.y),
|
||||
|
||||
var circleRadius = fabric.util.getRandomInt(
|
||||
Math.max(0, this.width - 20), this.width + 20) / 2;
|
||||
circleRadius = fabric.util.getRandomInt(
|
||||
Math.max(0, this.width - 20), this.width + 20) / 2,
|
||||
|
||||
var circleColor = new fabric.Color(this.color)
|
||||
circleColor = new fabric.Color(this.color)
|
||||
.setAlpha(fabric.util.getRandomInt(0, 100) / 100)
|
||||
.toRgba();
|
||||
|
||||
|
|
|
|||
|
|
@ -109,27 +109,27 @@
|
|||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
ctx.beginPath();
|
||||
|
||||
var p1 = this._points[0];
|
||||
var p2 = this._points[1];
|
||||
var p1 = this._points[0],
|
||||
p2 = this._points[1];
|
||||
|
||||
//if we only have 2 points in the path and they are the same
|
||||
//it means that the user only clicked the canvas without moving the mouse
|
||||
//then we should be drawing a dot. A path isn't drawn between two identical dots
|
||||
//that's why we set them apart a bit
|
||||
if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
|
||||
p1.x -= 0.5;
|
||||
p2.x += 0.5;
|
||||
p1.x -= 0.5;
|
||||
p2.x += 0.5;
|
||||
}
|
||||
ctx.moveTo(p1.x, p1.y);
|
||||
|
||||
for (var i = 1, len = this._points.length; i < len; i++) {
|
||||
// we pick the point between pi+1 & pi+2 as the
|
||||
// we pick the point between pi + 1 & pi + 2 as the
|
||||
// end point and p1 as our control point.
|
||||
var midPoint = p1.midPointFrom(p2);
|
||||
ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
|
||||
|
||||
p1 = this._points[i];
|
||||
p2 = this._points[i+1];
|
||||
p2 = this._points[i + 1];
|
||||
}
|
||||
// Draw last line as a straight line while
|
||||
// we wait for the next point to be able to calculate
|
||||
|
|
@ -155,7 +155,7 @@
|
|||
* @param {Array} points
|
||||
* @return {Object} object with minx, miny, maxx, maxy
|
||||
*/
|
||||
getPathBoundingBox: function(points) {
|
||||
getPathBoundingBox: function(points) {
|
||||
var xBounds = [],
|
||||
yBounds = [],
|
||||
p1 = points[0],
|
||||
|
|
@ -171,19 +171,19 @@
|
|||
yBounds.push(midPoint.y);
|
||||
|
||||
p1 = points[i];
|
||||
p2 = points[i+1];
|
||||
p2 = points[i + 1];
|
||||
startPoint = midPoint;
|
||||
} // end for
|
||||
}
|
||||
|
||||
xBounds.push(p1.x);
|
||||
yBounds.push(p1.y);
|
||||
xBounds.push(p1.x);
|
||||
yBounds.push(p1.y);
|
||||
|
||||
return {
|
||||
minx: utilMin(xBounds),
|
||||
miny: utilMin(yBounds),
|
||||
maxx: utilMax(xBounds),
|
||||
maxy: utilMax(yBounds)
|
||||
};
|
||||
return {
|
||||
minx: utilMin(xBounds),
|
||||
miny: utilMin(yBounds),
|
||||
maxx: utilMax(xBounds),
|
||||
maxy: utilMax(yBounds)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -192,9 +192,9 @@
|
|||
* @return {String} SVG path
|
||||
*/
|
||||
convertPointsToSVGPath: function(points, minX, maxX, minY) {
|
||||
var path = [];
|
||||
var p1 = new fabric.Point(points[0].x - minX, points[0].y - minY);
|
||||
var p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
|
||||
var path = [],
|
||||
p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
|
||||
p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
|
||||
|
||||
path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
|
||||
for (var i = 1, len = points.length; i < len; i++) {
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
// start point is p(i-1) value.
|
||||
path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
|
||||
p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
|
||||
if ((i+1) < points.length) {
|
||||
p2 = new fabric.Point(points[i+1].x - minX, points[i+1].y - minY);
|
||||
if ((i + 1) < points.length) {
|
||||
p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
|
||||
}
|
||||
}
|
||||
path.push('L ', p1.x, ' ', p1.y, ' ');
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
ctx.closePath();
|
||||
|
||||
var pathData = this._getSVGPathData().join('');
|
||||
if (pathData === "M 0 0 Q 0 0 0 0 L 0 0") {
|
||||
if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
|
||||
// do not create 0 width/height paths, as they are
|
||||
// rendered inconsistently across browsers
|
||||
// Firefox 4, for example, renders a dot,
|
||||
|
|
@ -254,8 +254,8 @@
|
|||
}
|
||||
|
||||
// set path origin coordinates based on our bounding box
|
||||
var originLeft = this.box.minx + (this.box.maxx - this.box.minx) /2;
|
||||
var originTop = this.box.miny + (this.box.maxy - this.box.miny) /2;
|
||||
var originLeft = this.box.minx + (this.box.maxx - this.box.minx) / 2,
|
||||
originTop = this.box.miny + (this.box.maxy - this.box.miny) / 2;
|
||||
|
||||
this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
|
||||
|
||||
|
|
|
|||
119
src/brushes/|
119
src/brushes/|
|
|
@ -1,119 +0,0 @@
|
|||
/**
|
||||
* CircleBrush class
|
||||
* @class fabric.CircleBrush
|
||||
*/
|
||||
fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
|
||||
|
||||
/**
|
||||
* Width of a brush
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
width: 10,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {fabric.Canvas} canvas
|
||||
* @return {fabric.CircleBrush} Instance of a circle brush
|
||||
*/
|
||||
initialize: function(canvas) {
|
||||
this.canvas = canvas;
|
||||
this.points = [ ];
|
||||
},
|
||||
/**
|
||||
* Invoked inside on mouse down and mouse move
|
||||
* @param {Object} pointer
|
||||
*/
|
||||
drawDot: function(pointer) {
|
||||
var point = this.addPoint(pointer);
|
||||
var ctx = this.canvas.contextTop;
|
||||
|
||||
var v = this.canvas.viewportTransform;
|
||||
ctx.save();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
ctx.fillStyle = point.fill;
|
||||
ctx.beginPath();
|
||||
ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked on mouse down
|
||||
*/
|
||||
onMouseDown: function(pointer) {
|
||||
this.points.length = 0;
|
||||
this.canvas.clearContext(this.canvas.contextTop);
|
||||
this._setShadow();
|
||||
this.drawDot(pointer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked on mouse move
|
||||
* @param {Object} pointer
|
||||
*/
|
||||
onMouseMove: function(pointer) {
|
||||
this.drawDot(pointer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked on mouse up
|
||||
*/
|
||||
onMouseUp: function() {
|
||||
var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
|
||||
this.canvas.renderOnAddRemove = false;
|
||||
|
||||
var circles = [ ];
|
||||
|
||||
for (var i = 0, len = this.points.length; i < len; i++) {
|
||||
var point = this.points[i];
|
||||
var circle = new fabric.Circle({
|
||||
radius: this.points[i].radius,
|
||||
left: point.x,
|
||||
top: point.y,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
fill: this.points[i].fill
|
||||
});
|
||||
|
||||
this.shadow && circle.setShadow(this.shadow);
|
||||
|
||||
circles.push(circle);
|
||||
}
|
||||
var group = new fabric.Group(circles, { originX: 'center', originY: 'center' });
|
||||
group.canvas = this.canvas;
|
||||
|
||||
this.canvas.add(group);
|
||||
this.canvas.fire('path:created', { path: group });
|
||||
|
||||
this.canvas.clearContext(this.canvas.contextTop);
|
||||
this._resetShadow();
|
||||
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
|
||||
this.canvas.renderAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} pointer
|
||||
* @return {fabric.Point} Just added pointer point
|
||||
*/
|
||||
addPoint: function(pointer) {
|
||||
var pointerPoint = new fabric.Point(pointer.x, pointer.y);
|
||||
|
||||
var circleRadius = fabric.util.getRandomInt(
|
||||
Math.max(0, this.width - 20), this.width + 20) / 2;
|
||||
|
||||
var circleColor = new fabric.Color(this.color)
|
||||
.setAlpha(fabric.util.getRandomInt(0, 100) / 100)
|
||||
.toRgba();
|
||||
|
||||
pointerPoint.radius = circleRadius;
|
||||
pointerPoint.fill = circleColor;
|
||||
|
||||
this.points.push(pointerPoint);
|
||||
|
||||
return pointerPoint;
|
||||
}
|
||||
});
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
* @fires mouse:down
|
||||
* @fires mouse:move
|
||||
* @fires mouse:up
|
||||
* @fires mouse:over
|
||||
* @fires mouse:out
|
||||
*
|
||||
*/
|
||||
fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
|
||||
|
|
@ -203,10 +205,10 @@
|
|||
var t = this._currentTransform;
|
||||
|
||||
t.target.set({
|
||||
'scaleX': t.original.scaleX,
|
||||
'scaleY': t.original.scaleY,
|
||||
'left': t.original.left,
|
||||
'top': t.original.top
|
||||
scaleX: t.original.scaleX,
|
||||
scaleY: t.original.scaleY,
|
||||
left: t.original.left,
|
||||
top: t.original.top
|
||||
});
|
||||
|
||||
if (this._shouldCenterTransform(e, t.target)) {
|
||||
|
|
@ -253,7 +255,7 @@
|
|||
|
||||
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
|
||||
// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
|
||||
return (target.containsPoint(xy) || target._findTargetCorner(e, this._offset));
|
||||
return (target.containsPoint(xy) || target._findTargetCorner(pointer));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -263,14 +265,12 @@
|
|||
var activeGroup = this.getActiveGroup(),
|
||||
x = pointer.x,
|
||||
y = pointer.y,
|
||||
isObjectInGroup = (
|
||||
activeGroup &&
|
||||
object.type !== 'group' &&
|
||||
activeGroup.contains(object)),
|
||||
lt;
|
||||
|
||||
var isObjectInGroup = (
|
||||
activeGroup &&
|
||||
object.type !== 'group' &&
|
||||
activeGroup.contains(object)
|
||||
);
|
||||
|
||||
if (isObjectInGroup) {
|
||||
lt = new fabric.Point(activeGroup.left, activeGroup.top);
|
||||
lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
|
||||
|
|
@ -405,11 +405,8 @@
|
|||
_setupCurrentTransform: function (e, target) {
|
||||
if (!target) return;
|
||||
|
||||
var corner = target._findTargetCorner(e, this._offset),
|
||||
pointer = fabric.util.transformPoint(
|
||||
getPointer(e, this.upperCanvasEl),
|
||||
fabric.util.invertTransform(this.viewportTransform)
|
||||
),
|
||||
var pointer = this.getPointer(e),
|
||||
corner = target._findTargetCorner(this.getPointer(e, true)),
|
||||
action = this._getActionFromCorner(target, corner),
|
||||
origin = this._getOriginFromCorner(target, corner);
|
||||
|
||||
|
|
@ -471,7 +468,6 @@
|
|||
*/
|
||||
_scaleObject: function (x, y, by) {
|
||||
var t = this._currentTransform,
|
||||
offset = this._offset,
|
||||
target = t.target,
|
||||
lockScalingX = target.get('lockScalingX'),
|
||||
lockScalingY = target.get('lockScalingY');
|
||||
|
|
@ -479,8 +475,8 @@
|
|||
if (lockScalingX && lockScalingY) return;
|
||||
|
||||
// Get the constraint point
|
||||
var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
|
||||
var localMouse = target.toLocalPoint(new fabric.Point(x - offset.left, y - offset.top), t.originX, t.originY);
|
||||
var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
|
||||
localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
|
||||
|
||||
this._setLocalMouse(localMouse, t);
|
||||
|
||||
|
|
@ -527,9 +523,8 @@
|
|||
*/
|
||||
_scaleObjectEqually: function(localMouse, target, transform) {
|
||||
|
||||
var dist = localMouse.y + localMouse.x;
|
||||
|
||||
var lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
|
||||
var dist = localMouse.y + localMouse.x,
|
||||
lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
|
||||
(target.width + (target.strokeWidth)) * transform.original.scaleX;
|
||||
|
||||
// We use transform.scaleX/Y instead of target.scaleX/Y
|
||||
|
|
@ -626,13 +621,12 @@
|
|||
*/
|
||||
_rotateObject: function (x, y) {
|
||||
|
||||
var t = this._currentTransform,
|
||||
o = this._offset;
|
||||
var t = this._currentTransform;
|
||||
|
||||
if (t.target.get('lockRotation')) return;
|
||||
|
||||
var lastAngle = atan2(t.ey - t.top - o.top, t.ex - t.left - o.left),
|
||||
curAngle = atan2(y - t.top - o.top, x - t.left - o.left),
|
||||
var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
|
||||
curAngle = atan2(y - t.top, x - t.left),
|
||||
angle = radiansToDegrees(curAngle - lastAngle + t.theta);
|
||||
|
||||
// normalize angle to positive value
|
||||
|
|
@ -685,15 +679,15 @@
|
|||
// selection border
|
||||
if (this.selectionDashArray.length > 1) {
|
||||
|
||||
var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft);
|
||||
var py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
|
||||
var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft),
|
||||
py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
fabric.util.drawDashedLine(ctx, px, py, px+aleft, py, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px, py+atop-1, px+aleft, py+atop-1, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px, py, px, py+atop, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px+aleft-1, py, px+aleft-1, py+atop, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
|
||||
fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
|
@ -717,7 +711,7 @@
|
|||
this.lastRenderedObjectWithControlsAboveOverlay &&
|
||||
this.lastRenderedObjectWithControlsAboveOverlay.visible &&
|
||||
this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
|
||||
this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e, this._offset));
|
||||
this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -735,10 +729,56 @@
|
|||
// first check current group (if one exists)
|
||||
var activeGroup = this.getActiveGroup();
|
||||
if (activeGroup && !skipGroup && this.containsPoint(e, activeGroup)) {
|
||||
console.log('AG', activeGroup);
|
||||
return activeGroup;
|
||||
}
|
||||
|
||||
return this._searchPossibleTargets(e);
|
||||
var target = this._searchPossibleTargets(e);
|
||||
this._fireOverOutEvents(target);
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_fireOverOutEvents: function(target) {
|
||||
if (target) {
|
||||
if (this._hoveredTarget !== target) {
|
||||
this.fire('mouse:over', { target: target });
|
||||
target.fire('mouseover');
|
||||
if (this._hoveredTarget) {
|
||||
this.fire('mouse:out', { target: this._hoveredTarget });
|
||||
this._hoveredTarget.fire('mouseout');
|
||||
}
|
||||
this._hoveredTarget = target;
|
||||
}
|
||||
}
|
||||
else if (this._hoveredTarget) {
|
||||
this.fire('mouse:out', { target: this._hoveredTarget });
|
||||
this._hoveredTarget.fire('mouseout');
|
||||
this._hoveredTarget = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_checkTarget: function(e, obj, pointer) {
|
||||
if (obj &&
|
||||
obj.visible &&
|
||||
obj.evented &&
|
||||
this.containsPoint(e, obj)){
|
||||
if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
|
||||
var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
|
||||
if (!isTransparent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -747,33 +787,15 @@
|
|||
_searchPossibleTargets: function(e) {
|
||||
|
||||
// Cache all targets where their bounding box contains point.
|
||||
var possibleTargets = [],
|
||||
target,
|
||||
var target,
|
||||
pointer = this.getPointer(e, true);
|
||||
|
||||
for (var i = this._objects.length; i--; ) {
|
||||
if (this._objects[i] &&
|
||||
this._objects[i].visible &&
|
||||
this._objects[i].evented &&
|
||||
this.containsPoint(e, this._objects[i])) {
|
||||
var i = this._objects.length;
|
||||
|
||||
if (this.perPixelTargetFind || this._objects[i].perPixelTargetFind) {
|
||||
possibleTargets[possibleTargets.length] = this._objects[i];
|
||||
}
|
||||
else {
|
||||
target = this._objects[i];
|
||||
this.relatedTarget = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0, len = possibleTargets.length; j < len; j++) {
|
||||
pointer = this.getPointer(e, true);
|
||||
var isTransparent = this.isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
|
||||
if (!isTransparent) {
|
||||
target = possibleTargets[j];
|
||||
this.relatedTarget = target;
|
||||
while (i--) {
|
||||
if (this._checkTarget(e, this._objects[i], pointer)){
|
||||
this.relatedTarget = this._objects[i];
|
||||
target = this._objects[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -790,7 +812,12 @@
|
|||
if (!upperCanvasEl) {
|
||||
upperCanvasEl = this.upperCanvasEl;
|
||||
}
|
||||
var pointer = getPointer(e, upperCanvasEl);
|
||||
var pointer = getPointer(e, upperCanvasEl),
|
||||
bounds = upperCanvasEl.getBoundingClientRect(),
|
||||
cssScale;
|
||||
|
||||
pointer.x = pointer.x - this._offset.left;
|
||||
pointer.y = pointer.y - this._offset.top;
|
||||
if (!ignoreZoom) {
|
||||
pointer = fabric.util.transformPoint(
|
||||
pointer,
|
||||
|
|
@ -798,9 +825,19 @@
|
|||
);
|
||||
}
|
||||
|
||||
if (bounds.width === 0 || bounds.height === 0) {
|
||||
// If bounds are not available (i.e. not visible), do not apply scale.
|
||||
cssScale = { width: 1, height: 1 };
|
||||
}
|
||||
else {
|
||||
cssScale = {
|
||||
width: upperCanvasEl.width / bounds.width,
|
||||
height: upperCanvasEl.height / bounds.height
|
||||
};
|
||||
}
|
||||
return {
|
||||
x: pointer.x - this._offset.left,
|
||||
y: pointer.y - this._offset.top
|
||||
x: pointer.x * cssScale.width,
|
||||
y: pointer.y * cssScale.height
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
@ -43,6 +43,11 @@
|
|||
color = Color.colorNameMap[color];
|
||||
}
|
||||
|
||||
if (color === 'transparent') {
|
||||
this.setSource([255,255,255,0]);
|
||||
return;
|
||||
}
|
||||
|
||||
source = Color.sourceFromHex(color);
|
||||
|
||||
if (!source) {
|
||||
|
|
@ -161,15 +166,15 @@
|
|||
* @return {String} ex: FF5555
|
||||
*/
|
||||
toHex: function() {
|
||||
var source = this.getSource();
|
||||
var source = this.getSource(), r, g, b;
|
||||
|
||||
var r = source[0].toString(16);
|
||||
r = source[0].toString(16);
|
||||
r = (r.length === 1) ? ('0' + r) : r;
|
||||
|
||||
var g = source[1].toString(16);
|
||||
g = source[1].toString(16);
|
||||
g = (g.length === 1) ? ('0' + g) : g;
|
||||
|
||||
var b = source[2].toString(16);
|
||||
b = source[2].toString(16);
|
||||
b = (b.length === 1) ? ('0' + b) : b;
|
||||
|
||||
return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
|
||||
|
|
@ -256,7 +261,7 @@
|
|||
* @field
|
||||
* @memberOf fabric.Color
|
||||
*/
|
||||
fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
|
||||
fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
|
||||
|
||||
/**
|
||||
* Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
|
||||
|
|
@ -282,23 +287,23 @@
|
|||
* @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
|
||||
*/
|
||||
fabric.Color.colorNameMap = {
|
||||
'aqua': '#00FFFF',
|
||||
'black': '#000000',
|
||||
'blue': '#0000FF',
|
||||
'fuchsia': '#FF00FF',
|
||||
'gray': '#808080',
|
||||
'green': '#008000',
|
||||
'lime': '#00FF00',
|
||||
'maroon': '#800000',
|
||||
'navy': '#000080',
|
||||
'olive': '#808000',
|
||||
'orange': '#FFA500',
|
||||
'purple': '#800080',
|
||||
'red': '#FF0000',
|
||||
'silver': '#C0C0C0',
|
||||
'teal': '#008080',
|
||||
'white': '#FFFFFF',
|
||||
'yellow': '#FFFF00'
|
||||
aqua: '#00FFFF',
|
||||
black: '#000000',
|
||||
blue: '#0000FF',
|
||||
fuchsia: '#FF00FF',
|
||||
gray: '#808080',
|
||||
green: '#008000',
|
||||
lime: '#00FF00',
|
||||
maroon: '#800000',
|
||||
navy: '#000080',
|
||||
olive: '#808000',
|
||||
orange: '#FFA500',
|
||||
purple: '#800080',
|
||||
red: '#FF0000',
|
||||
silver: '#C0C0C0',
|
||||
teal: '#008080',
|
||||
white: '#FFFFFF',
|
||||
yellow: '#FFFF00'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -309,11 +314,21 @@
|
|||
* @return {Number}
|
||||
*/
|
||||
function hue2rgb(p, q, t){
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1/6) return p + (q - p) * 6 * t;
|
||||
if (t < 1/2) return q;
|
||||
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
}
|
||||
if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1/6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1/2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2/3) {
|
||||
return p + (q - p) * (2/3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
@ -390,8 +405,8 @@
|
|||
r = g = b = l;
|
||||
}
|
||||
else {
|
||||
var q = l <= 0.5 ? l * (s + 1) : l + s - l * s;
|
||||
var p = l * 2 - q;
|
||||
var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
|
||||
p = l * 2 - q;
|
||||
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
|
|
|
|||
|
|
@ -1,67 +1,69 @@
|
|||
fabric.ElementsParser = {
|
||||
fabric.ElementsParser = function(elements, callback, options, reviver) {
|
||||
this.elements = elements;
|
||||
this.callback = callback;
|
||||
this.options = options;
|
||||
this.reviver = reviver;
|
||||
};
|
||||
|
||||
parse: function(elements, callback, options, reviver) {
|
||||
fabric.ElementsParser.prototype.parse = function() {
|
||||
this.instances = new Array(this.elements.length);
|
||||
this.numElements = this.elements.length;
|
||||
|
||||
this.elements = elements;
|
||||
this.callback = callback;
|
||||
this.options = options;
|
||||
this.reviver = reviver;
|
||||
this.createObjects();
|
||||
};
|
||||
|
||||
this.instances = new Array(elements.length);
|
||||
this.numElements = elements.length;
|
||||
|
||||
this.createObjects();
|
||||
},
|
||||
|
||||
createObjects: function() {
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
this.createObject(this.elements[i], i);
|
||||
}
|
||||
},
|
||||
|
||||
createObject: function(el, index) {
|
||||
var klass = fabric[fabric.util.string.capitalize(el.tagName)];
|
||||
if (klass && klass.fromElement) {
|
||||
try {
|
||||
this._createObject(klass, el, index);
|
||||
}
|
||||
catch(err) {
|
||||
fabric.log(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.checkIfDone();
|
||||
}
|
||||
},
|
||||
|
||||
_createObject: function(klass, el, index) {
|
||||
if (klass.async) {
|
||||
klass.fromElement(el, this.createCallback(index, el), this.options);
|
||||
}
|
||||
else {
|
||||
var obj = klass.fromElement(el, this.options);
|
||||
this.reviver && this.reviver(el, obj);
|
||||
this.instances.splice(index, 0, obj);
|
||||
this.checkIfDone();
|
||||
}
|
||||
},
|
||||
|
||||
createCallback: function(index, el) {
|
||||
var _this = this;
|
||||
return function(obj) {
|
||||
_this.reviver && _this.reviver(el, obj);
|
||||
_this.instances.splice(index, 0, obj);
|
||||
_this.checkIfDone();
|
||||
};
|
||||
},
|
||||
|
||||
checkIfDone: function() {
|
||||
if (--this.numElements === 0) {
|
||||
this.instances = this.instances.filter(function(el) {
|
||||
return el != null;
|
||||
});
|
||||
fabric.resolveGradients(this.instances);
|
||||
this.callback(this.instances);
|
||||
}
|
||||
fabric.ElementsParser.prototype.createObjects = function() {
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
(function(_this, i) {
|
||||
setTimeout(function() {
|
||||
_this.createObject(_this.elements[i], i);
|
||||
}, 0);
|
||||
})(this, i);
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.createObject = function(el, index) {
|
||||
var klass = fabric[fabric.util.string.capitalize(el.tagName)];
|
||||
if (klass && klass.fromElement) {
|
||||
try {
|
||||
this._createObject(klass, el, index);
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.checkIfDone();
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
|
||||
if (klass.async) {
|
||||
klass.fromElement(el, this.createCallback(index, el), this.options);
|
||||
}
|
||||
else {
|
||||
var obj = klass.fromElement(el, this.options);
|
||||
this.reviver && this.reviver(el, obj);
|
||||
this.instances.splice(index, 0, obj);
|
||||
this.checkIfDone();
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.createCallback = function(index, el) {
|
||||
var _this = this;
|
||||
return function(obj) {
|
||||
_this.reviver && _this.reviver(el, obj);
|
||||
_this.instances.splice(index, 0, obj);
|
||||
_this.checkIfDone();
|
||||
};
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.checkIfDone = function() {
|
||||
if (--this.numElements === 0) {
|
||||
this.instances = this.instances.filter(function(el) {
|
||||
return el != null;
|
||||
});
|
||||
fabric.resolveGradients(this.instances);
|
||||
this.callback(this.instances);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
fabric.Image.filters = fabric.Image.filters || { };
|
||||
|
||||
|
||||
/**
|
||||
* Root filter class from which all filter classes inherit from
|
||||
* @class fabric.Image.filters.BaseFilter
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -32,11 +32,11 @@
|
|||
* Constructor
|
||||
* @memberOf fabric.Image.filters.Brightness.prototype
|
||||
* @param {Object} [options] Options object
|
||||
* @param {Number} [options.brightness=100] Value to brighten the image up (0..255)
|
||||
* @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
|
||||
*/
|
||||
initialize: function(options) {
|
||||
options = options || { };
|
||||
this.brightness = options.brightness || 100;
|
||||
this.brightness = options.brightness || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -66,9 +66,11 @@
|
|||
options = options || { };
|
||||
|
||||
this.opaque = options.opaque;
|
||||
this.matrix = options.matrix || [ 0, 0, 0,
|
||||
this.matrix = options.matrix || [
|
||||
0, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 0 ];
|
||||
0, 0, 0
|
||||
];
|
||||
|
||||
var canvasEl = fabric.util.createCanvasElement();
|
||||
this.tmpCtx = canvasEl.getContext('2d');
|
||||
|
|
@ -95,49 +97,49 @@
|
|||
halfSide = Math.floor(side/2),
|
||||
src = pixels.data,
|
||||
sw = pixels.width,
|
||||
sh = pixels.height;
|
||||
sh = pixels.height,
|
||||
|
||||
// pad output by the convolution matrix
|
||||
var w = sw;
|
||||
var h = sh;
|
||||
var output = this._createImageData(w, h);
|
||||
// pad output by the convolution matrix
|
||||
w = sw,
|
||||
h = sh,
|
||||
output = this._createImageData(w, h),
|
||||
|
||||
var dst = output.data;
|
||||
dst = output.data,
|
||||
|
||||
// go through the destination image pixels
|
||||
var alphaFac = this.opaque ? 1 : 0;
|
||||
// go through the destination image pixels
|
||||
alphaFac = this.opaque ? 1 : 0;
|
||||
|
||||
for (var y=0; y<h; y++) {
|
||||
for (var x=0; x<w; x++) {
|
||||
var sy = y;
|
||||
var sx = x;
|
||||
var dstOff = (y*w+x)*4;
|
||||
// calculate the weighed sum of the source image pixels that
|
||||
// fall under the convolution matrix
|
||||
var r=0, g=0, b=0, a=0;
|
||||
for (var y = 0; y < h; y++) {
|
||||
for (var x = 0; x < w; x++) {
|
||||
var sy = y,
|
||||
sx = x,
|
||||
dstOff = (y * w + x) * 4,
|
||||
// calculate the weighed sum of the source image pixels that
|
||||
// fall under the convolution matrix
|
||||
r = 0, g = 0, b = 0, a = 0;
|
||||
|
||||
for (var cy=0; cy<side; cy++) {
|
||||
for (var cx=0; cx<side; cx++) {
|
||||
for (var cy = 0; cy < side; cy++) {
|
||||
for (var cx = 0; cx < side; cx++) {
|
||||
|
||||
var scy = sy + cy - halfSide;
|
||||
var scx = sx + cx - halfSide;
|
||||
var scy = sy + cy - halfSide,
|
||||
scx = sx + cx - halfSide;
|
||||
|
||||
/* jshint maxdepth:5 */
|
||||
if (scy < 0 || scy > sh || scx < 0 || scx > sw) continue;
|
||||
|
||||
var srcOff = (scy*sw+scx)*4;
|
||||
var wt = weights[cy*side+cx];
|
||||
var srcOff = (scy * sw + scx) * 4,
|
||||
wt = weights[cy * side + cx];
|
||||
|
||||
r += src[srcOff] * wt;
|
||||
g += src[srcOff+1] * wt;
|
||||
b += src[srcOff+2] * wt;
|
||||
a += src[srcOff+3] * wt;
|
||||
g += src[srcOff + 1] * wt;
|
||||
b += src[srcOff + 2] * wt;
|
||||
a += src[srcOff + 3] * wt;
|
||||
}
|
||||
}
|
||||
dst[dstOff] = r;
|
||||
dst[dstOff+1] = g;
|
||||
dst[dstOff+2] = b;
|
||||
dst[dstOff+3] = a + alphaFac*(255-a);
|
||||
dst[dstOff + 1] = g;
|
||||
dst[dstOff + 2] = b;
|
||||
dst[dstOff + 3] = a + alphaFac * (255 - a);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +165,7 @@
|
|||
* @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
|
||||
*/
|
||||
fabric.Image.filters.Convolute.fromObject = function(object) {
|
||||
return new fabric.Image.filters.Convolute(object);
|
||||
return new fabric.Image.filters.Convolute(object);
|
||||
};
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
|
|||
92
src/filters/multiply_filter.class.js
Normal file
92
src/filters/multiply_filter.class.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
(function(global) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
||||
/**
|
||||
* Multiply filter class
|
||||
* Adapted from <a href="http://www.laurenscorijn.com/articles/colormath-basics">http://www.laurenscorijn.com/articles/colormath-basics</a>
|
||||
* @class fabric.Image.filters.Multiply
|
||||
* @memberOf fabric.Image.filters
|
||||
* @extends fabric.Image.filters.BaseFilter
|
||||
* @example <caption>Multiply filter with hex color</caption>
|
||||
* var filter = new fabric.Image.filters.Multiply({
|
||||
* color: '#F0F'
|
||||
* });
|
||||
* object.filters.push(filter);
|
||||
* object.applyFilters(canvas.renderAll.bind(canvas));
|
||||
* @example <caption>Multiply filter with rgb color</caption>
|
||||
* var filter = new fabric.Image.filters.Multiply({
|
||||
* color: 'rgb(53, 21, 176)'
|
||||
* });
|
||||
* object.filters.push(filter);
|
||||
* object.applyFilters(canvas.renderAll.bind(canvas));
|
||||
*/
|
||||
fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
|
||||
|
||||
/**
|
||||
* Filter type
|
||||
* @param {String} type
|
||||
* @default
|
||||
*/
|
||||
type: 'Multiply',
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @memberOf fabric.Image.filters.Multiply.prototype
|
||||
* @param {Object} [options] Options object
|
||||
* @param {String} [options.color=#000000] Color to multiply the image pixels with
|
||||
*/
|
||||
initialize: function(options) {
|
||||
options = options || { };
|
||||
|
||||
this.color = options.color || '#000000';
|
||||
},
|
||||
|
||||
/**
|
||||
* Applies filter to canvas element
|
||||
* @param {Object} canvasEl Canvas element to apply filter to
|
||||
*/
|
||||
applyTo: function(canvasEl) {
|
||||
var context = canvasEl.getContext('2d'),
|
||||
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
||||
data = imageData.data,
|
||||
iLen = data.length, i,
|
||||
source;
|
||||
|
||||
source = new fabric.Color(this.color).getSource();
|
||||
|
||||
for (i = 0; i < iLen; i+=4) {
|
||||
data[i] *= source[0]/255;
|
||||
data[i + 1] *= source[1]/255;
|
||||
data[i + 2] *= source[2]/255;
|
||||
|
||||
}
|
||||
|
||||
context.putImageData(imageData, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns object representation of an instance
|
||||
* @return {Object} Object representation of an instance
|
||||
*/
|
||||
toObject: function() {
|
||||
return extend(this.callSuper('toObject'), {
|
||||
color: this.color
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns filter instance from an object representation
|
||||
* @static
|
||||
* @param {Object} object Object to create an instance from
|
||||
* @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
|
||||
*/
|
||||
fabric.Image.filters.Multiply.fromObject = function(object) {
|
||||
return new fabric.Image.filters.Multiply(object);
|
||||
};
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -32,11 +32,11 @@
|
|||
* Constructor
|
||||
* @memberOf fabric.Image.filters.Noise.prototype
|
||||
* @param {Object} [options] Options object
|
||||
* @param {Number} [options.noise=100] Noise value
|
||||
* @param {Number} [options.noise=0] Noise value
|
||||
*/
|
||||
initialize: function(options) {
|
||||
options = options || { };
|
||||
this.noise = options.noise || 100;
|
||||
this.noise = options.noise || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -57,9 +57,9 @@
|
|||
index = (i * 4) * jLen + (j * 4);
|
||||
|
||||
r = data[index];
|
||||
g = data[index+1];
|
||||
b = data[index+2];
|
||||
a = data[index+3];
|
||||
g = data[index + 1];
|
||||
b = data[index + 2];
|
||||
a = data[index + 3];
|
||||
|
||||
/*
|
||||
blocksize: 4
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -58,17 +58,17 @@
|
|||
|
||||
for (var i = 0, len = data.length; i < len; i += 4) {
|
||||
r = data[i];
|
||||
g = data[i+1];
|
||||
b = data[i+2];
|
||||
g = data[i + 1];
|
||||
b = data[i + 2];
|
||||
|
||||
if (r > limit &&
|
||||
g > limit &&
|
||||
b > limit &&
|
||||
abs(r-g) < distance &&
|
||||
abs(r-b) < distance &&
|
||||
abs(g-b) < distance
|
||||
abs(r - g) < distance &&
|
||||
abs(r - b) < distance &&
|
||||
abs(g - b) < distance
|
||||
) {
|
||||
data[i+3] = 1;
|
||||
data[i + 3] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
if (style) {
|
||||
var keyValuePairs = style.split(/\s*;\s*/);
|
||||
|
||||
if (keyValuePairs[keyValuePairs.length-1] === '') {
|
||||
if (keyValuePairs[keyValuePairs.length - 1] === '') {
|
||||
keyValuePairs.pop();
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,11 @@
|
|||
addColorStop: function(colorStop) {
|
||||
for (var position in colorStop) {
|
||||
var color = new fabric.Color(colorStop[position]);
|
||||
this.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
|
||||
this.colorStops.push({
|
||||
offset: position,
|
||||
color: color.toRgb(),
|
||||
opacity: color.getAlpha()
|
||||
});
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,161 +1,160 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
if (fabric.Intersection) {
|
||||
fabric.warn('fabric.Intersection is already defined');
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection class
|
||||
* @class fabric.Intersection
|
||||
* @memberOf fabric
|
||||
* @constructor
|
||||
*/
|
||||
function Intersection(status) {
|
||||
this.status = status;
|
||||
this.points = [];
|
||||
}
|
||||
|
||||
fabric.Intersection = Intersection;
|
||||
|
||||
fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
|
||||
|
||||
/**
|
||||
* Appends a point to intersection
|
||||
* @param {fabric.Point} point
|
||||
*/
|
||||
appendPoint: function (point) {
|
||||
this.points.push(point);
|
||||
},
|
||||
|
||||
/**
|
||||
* Appends points to intersection
|
||||
* @param {Array} points
|
||||
*/
|
||||
appendPoints: function (points) {
|
||||
this.points = this.points.concat(points);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if one line intersects another
|
||||
* @static
|
||||
* @param {fabric.Point} a1
|
||||
* @param {fabric.Point} a2
|
||||
* @param {fabric.Point} b1
|
||||
* @param {fabric.Point} b2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
|
||||
var result,
|
||||
ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
|
||||
ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
|
||||
u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
|
||||
if (u_b !== 0) {
|
||||
var ua = ua_t / u_b,
|
||||
ub = ub_t / u_b;
|
||||
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
|
||||
result = new Intersection("Intersection");
|
||||
result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
|
||||
}
|
||||
else {
|
||||
result = new Intersection();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ua_t === 0 || ub_t === 0) {
|
||||
result = new Intersection("Coincident");
|
||||
}
|
||||
else {
|
||||
result = new Intersection("Parallel");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if line intersects polygon
|
||||
* @static
|
||||
* @param {fabric.Point} a1
|
||||
* @param {fabric.Point} a2
|
||||
* @param {Array} points
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
|
||||
var result = new Intersection(),
|
||||
length = points.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var b1 = points[i],
|
||||
b2 = points[(i+1) % length],
|
||||
inter = Intersection.intersectLineLine(a1, a2, b1, b2);
|
||||
|
||||
result.appendPoints(inter.points);
|
||||
}
|
||||
if (result.points.length > 0) {
|
||||
result.status = "Intersection";
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if polygon intersects another polygon
|
||||
* @static
|
||||
* @param {Array} points1
|
||||
* @param {Array} points2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
|
||||
var result = new Intersection(),
|
||||
length = points1.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var a1 = points1[i],
|
||||
a2 = points1[(i+1) % length],
|
||||
inter = Intersection.intersectLinePolygon(a1, a2, points2);
|
||||
|
||||
result.appendPoints(inter.points);
|
||||
}
|
||||
if (result.points.length > 0) {
|
||||
result.status = "Intersection";
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if polygon intersects rectangle
|
||||
* @static
|
||||
* @param {Array} points
|
||||
* @param {Number} r1
|
||||
* @param {Number} r2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
|
||||
var min = r1.min(r2),
|
||||
max = r1.max(r2),
|
||||
topRight = new fabric.Point(max.x, min.y),
|
||||
bottomLeft = new fabric.Point(min.x, max.y),
|
||||
inter1 = Intersection.intersectLinePolygon(min, topRight, points),
|
||||
inter2 = Intersection.intersectLinePolygon(topRight, max, points),
|
||||
inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
|
||||
inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
|
||||
result = new Intersection();
|
||||
|
||||
result.appendPoints(inter1.points);
|
||||
result.appendPoints(inter2.points);
|
||||
result.appendPoints(inter3.points);
|
||||
result.appendPoints(inter4.points);
|
||||
|
||||
if (result.points.length > 0) {
|
||||
result.status = "Intersection";
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
(function(global) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
if (fabric.Intersection) {
|
||||
fabric.warn('fabric.Intersection is already defined');
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection class
|
||||
* @class fabric.Intersection
|
||||
* @memberOf fabric
|
||||
* @constructor
|
||||
*/
|
||||
function Intersection(status) {
|
||||
this.status = status;
|
||||
this.points = [];
|
||||
}
|
||||
|
||||
fabric.Intersection = Intersection;
|
||||
|
||||
fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
|
||||
|
||||
/**
|
||||
* Appends a point to intersection
|
||||
* @param {fabric.Point} point
|
||||
*/
|
||||
appendPoint: function (point) {
|
||||
this.points.push(point);
|
||||
},
|
||||
|
||||
/**
|
||||
* Appends points to intersection
|
||||
* @param {Array} points
|
||||
*/
|
||||
appendPoints: function (points) {
|
||||
this.points = this.points.concat(points);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if one line intersects another
|
||||
* @static
|
||||
* @param {fabric.Point} a1
|
||||
* @param {fabric.Point} a2
|
||||
* @param {fabric.Point} b1
|
||||
* @param {fabric.Point} b2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
|
||||
var result,
|
||||
uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
|
||||
ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
|
||||
uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
|
||||
if (uB !== 0) {
|
||||
var ua = uaT / uB,
|
||||
ub = ubT / uB;
|
||||
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
|
||||
result = new Intersection('Intersection');
|
||||
result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
|
||||
}
|
||||
else {
|
||||
result = new Intersection();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (uaT === 0 || ubT === 0) {
|
||||
result = new Intersection('Coincident');
|
||||
}
|
||||
else {
|
||||
result = new Intersection('Parallel');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if line intersects polygon
|
||||
* @static
|
||||
* @param {fabric.Point} a1
|
||||
* @param {fabric.Point} a2
|
||||
* @param {Array} points
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
|
||||
var result = new Intersection(),
|
||||
length = points.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var b1 = points[i],
|
||||
b2 = points[(i + 1) % length],
|
||||
inter = Intersection.intersectLineLine(a1, a2, b1, b2);
|
||||
|
||||
result.appendPoints(inter.points);
|
||||
}
|
||||
if (result.points.length > 0) {
|
||||
result.status = 'Intersection';
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if polygon intersects another polygon
|
||||
* @static
|
||||
* @param {Array} points1
|
||||
* @param {Array} points2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
|
||||
var result = new Intersection(),
|
||||
length = points1.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var a1 = points1[i],
|
||||
a2 = points1[(i + 1) % length],
|
||||
inter = Intersection.intersectLinePolygon(a1, a2, points2);
|
||||
|
||||
result.appendPoints(inter.points);
|
||||
}
|
||||
if (result.points.length > 0) {
|
||||
result.status = 'Intersection';
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if polygon intersects rectangle
|
||||
* @static
|
||||
* @param {Array} points
|
||||
* @param {Number} r1
|
||||
* @param {Number} r2
|
||||
* @return {fabric.Intersection}
|
||||
*/
|
||||
fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
|
||||
var min = r1.min(r2),
|
||||
max = r1.max(r2),
|
||||
topRight = new fabric.Point(max.x, min.y),
|
||||
bottomLeft = new fabric.Point(min.x, max.y),
|
||||
inter1 = Intersection.intersectLinePolygon(min, topRight, points),
|
||||
inter2 = Intersection.intersectLinePolygon(topRight, max, points),
|
||||
inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
|
||||
inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
|
||||
result = new Intersection();
|
||||
|
||||
result.appendPoints(inter1.points);
|
||||
result.appendPoints(inter2.points);
|
||||
result.appendPoints(inter3.points);
|
||||
result.appendPoints(inter4.points);
|
||||
|
||||
if (result.points.length > 0) {
|
||||
result.status = 'Intersection';
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
for (prop in arguments[0]) {
|
||||
propsToAnimate.push(prop);
|
||||
}
|
||||
for (var i = 0, len = propsToAnimate.length; i<len; i++) {
|
||||
for (var i = 0, len = propsToAnimate.length; i < len; i++) {
|
||||
prop = propsToAnimate[i];
|
||||
skipCallbacks = i !== len - 1;
|
||||
this._animate(prop, arguments[0][prop], arguments[1], skipCallbacks);
|
||||
|
|
@ -162,7 +162,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked
|
||||
*/
|
||||
_animate: function(property, to, options, skipCallbacks) {
|
||||
var obj = this, propPair;
|
||||
var _this = this, propPair;
|
||||
|
||||
to = to.toString();
|
||||
|
||||
|
|
@ -199,14 +199,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
easing: options.easing,
|
||||
duration: options.duration,
|
||||
abort: options.abort && function() {
|
||||
return options.abort.call(obj);
|
||||
return options.abort.call(_this);
|
||||
},
|
||||
onChange: function(value) {
|
||||
if (propPair) {
|
||||
obj[propPair[0]][propPair[1]] = value;
|
||||
_this[propPair[0]][propPair[1]] = value;
|
||||
}
|
||||
else {
|
||||
obj.set(property, value);
|
||||
_this.set(property, value);
|
||||
}
|
||||
if (skipCallbacks) return;
|
||||
options.onChange && options.onChange();
|
||||
|
|
@ -214,7 +214,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
onComplete: function() {
|
||||
if (skipCallbacks) return;
|
||||
|
||||
obj.setCoords();
|
||||
_this.setCoords();
|
||||
options.onComplete && options.onComplete();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
|
||||
this.renderAll(true);
|
||||
|
||||
var canvasEl = this.upperCanvasEl || this.lowerCanvasEl;
|
||||
var croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
|
||||
var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
|
||||
croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
|
||||
|
||||
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
|
||||
if (format === 'jpg') {
|
||||
|
|
@ -87,9 +87,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
__getCroppedCanvas: function(canvasEl, cropping) {
|
||||
|
||||
var croppedCanvasEl,
|
||||
croppedCtx;
|
||||
|
||||
var shouldCrop = 'left' in cropping ||
|
||||
croppedCtx,
|
||||
shouldCrop = 'left' in cropping ||
|
||||
'top' in cropping ||
|
||||
'width' in cropping ||
|
||||
'height' in cropping;
|
||||
|
|
@ -122,7 +121,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
|
||||
ctx = this.contextTop || this.contextContainer;
|
||||
|
||||
this.setWidth(scaledWidth).setHeight(scaledHeight);
|
||||
if (multiplier > 1) {
|
||||
this.setWidth(scaledWidth).setHeight(scaledHeight);
|
||||
}
|
||||
ctx.scale(multiplier, multiplier);
|
||||
|
||||
if (cropping.left) {
|
||||
|
|
@ -134,9 +135,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
if (cropping.width) {
|
||||
cropping.width *= multiplier;
|
||||
}
|
||||
else if (multiplier < 1) {
|
||||
cropping.width = scaledWidth;
|
||||
}
|
||||
if (cropping.height) {
|
||||
cropping.height *= multiplier;
|
||||
}
|
||||
else if (multiplier < 1) {
|
||||
cropping.height = scaledHeight;
|
||||
}
|
||||
|
||||
if (activeGroup) {
|
||||
// not removing group due to complications with restoring it with correct state afterwords
|
||||
|
|
|
|||
|
|
@ -1,31 +1,35 @@
|
|||
(function(){
|
||||
|
||||
var cursorMap = [
|
||||
'n-resize',
|
||||
'ne-resize',
|
||||
'e-resize',
|
||||
'se-resize',
|
||||
's-resize',
|
||||
'sw-resize',
|
||||
'w-resize',
|
||||
'nw-resize'
|
||||
],
|
||||
cursorOffset = {
|
||||
'mt': 0, // n
|
||||
'tr': 1, // ne
|
||||
'mr': 2, // e
|
||||
'br': 3, // se
|
||||
'mb': 4, // s
|
||||
'bl': 5, // sw
|
||||
'ml': 6, // w
|
||||
'tl': 7 // nw
|
||||
var cursorOffset = {
|
||||
mt: 0, // n
|
||||
tr: 1, // ne
|
||||
mr: 2, // e
|
||||
br: 3, // se
|
||||
mb: 4, // s
|
||||
bl: 5, // sw
|
||||
ml: 6, // w
|
||||
tl: 7 // nw
|
||||
},
|
||||
addListener = fabric.util.addListener,
|
||||
removeListener = fabric.util.removeListener,
|
||||
getPointer = fabric.util.getPointer;
|
||||
removeListener = fabric.util.removeListener;
|
||||
|
||||
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
|
||||
|
||||
/**
|
||||
* Map of cursor style values for each of the object controls
|
||||
* @private
|
||||
*/
|
||||
cursorMap: [
|
||||
'n-resize',
|
||||
'ne-resize',
|
||||
'e-resize',
|
||||
'se-resize',
|
||||
's-resize',
|
||||
'sw-resize',
|
||||
'w-resize',
|
||||
'nw-resize'
|
||||
],
|
||||
|
||||
/**
|
||||
* Adds mouse listeners to canvas
|
||||
* @private
|
||||
|
|
@ -141,14 +145,20 @@
|
|||
_onMouseDown: function (e) {
|
||||
this.__onMouseDown(e);
|
||||
|
||||
addListener(fabric.document, 'mouseup', this._onMouseUp);
|
||||
addListener(fabric.document, 'touchend', this._onMouseUp);
|
||||
|
||||
addListener(fabric.document, 'mousemove', this._onMouseMove);
|
||||
addListener(fabric.document, 'touchmove', this._onMouseMove);
|
||||
|
||||
removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
|
||||
removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
|
||||
|
||||
if (e.type === 'touchstart') {
|
||||
// Unbind mousedown to prevent double triggers from touch devices
|
||||
removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
|
||||
}
|
||||
else {
|
||||
addListener(fabric.document, 'mouseup', this._onMouseUp);
|
||||
addListener(fabric.document, 'mousemove', this._onMouseMove);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -166,6 +176,15 @@
|
|||
|
||||
addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
|
||||
addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
|
||||
|
||||
if (e.type === 'touchend') {
|
||||
// Wait 400ms before rebinding mousedown to prevent double triggers
|
||||
// from touch devices
|
||||
var _this = this;
|
||||
setTimeout(function() {
|
||||
addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
|
||||
}, 400);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -264,8 +283,8 @@
|
|||
*/
|
||||
_finalizeCurrentTransform: function() {
|
||||
|
||||
var transform = this._currentTransform;
|
||||
var target = transform.target;
|
||||
var transform = this._currentTransform,
|
||||
target = transform.target;
|
||||
|
||||
if (target._scaling) {
|
||||
target._scaling = false;
|
||||
|
|
@ -407,7 +426,7 @@
|
|||
this.stateful && target.saveState();
|
||||
|
||||
// determine if it's a drag or rotate case
|
||||
if ((corner = target._findTargetCorner(e, this._offset))) {
|
||||
if ((corner = target._findTargetCorner(this.getPointer(e)))) {
|
||||
this.onBeforeScaleRotate(target);
|
||||
}
|
||||
|
||||
|
|
@ -529,11 +548,11 @@
|
|||
* @param {Event} e Event fired on mousemove
|
||||
*/
|
||||
_transformObject: function(e) {
|
||||
|
||||
var pointer = fabric.util.transformPoint(
|
||||
getPointer(e, this.upperCanvasEl),
|
||||
fabric.util.getPointer(e, this.upperCanvasEl),
|
||||
fabric.util.invertTransform(this.viewportTransform)
|
||||
),
|
||||
pointer = this.getPointer(e),
|
||||
transform = this._currentTransform;
|
||||
|
||||
transform.reset = false,
|
||||
|
|
@ -581,7 +600,7 @@
|
|||
* @private
|
||||
*/
|
||||
_fire: function(eventName, target, e) {
|
||||
this.fire('object:' + eventName, { target: target, e: e});
|
||||
this.fire('object:' + eventName, { target: target, e: e });
|
||||
target.fire(eventName, { e: e });
|
||||
},
|
||||
|
||||
|
|
@ -638,11 +657,11 @@
|
|||
return false;
|
||||
}
|
||||
else {
|
||||
var activeGroup = this.getActiveGroup();
|
||||
// only show proper corner when group selection is not active
|
||||
var corner = target._findTargetCorner
|
||||
var activeGroup = this.getActiveGroup(),
|
||||
// only show proper corner when group selection is not active
|
||||
corner = target._findTargetCorner
|
||||
&& (!activeGroup || !activeGroup.contains(target))
|
||||
&& target._findTargetCorner(e, this._offset);
|
||||
&& target._findTargetCorner(this.getPointer(e, true));
|
||||
|
||||
if (!corner) {
|
||||
style.cursor = target.hoverCursor || this.hoverCursor;
|
||||
|
|
@ -685,7 +704,7 @@
|
|||
// normalize n to be from 0 to 7
|
||||
n %= 8;
|
||||
|
||||
return cursorMap[n];
|
||||
return this.cursorMap[n];
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
__onTransformGesture: function(e, self) {
|
||||
|
||||
if (this.isDrawingMode || e.touches.length !== 2 || 'gesture' !== self.gesture) {
|
||||
if (this.isDrawingMode || !e.touches || e.touches.length !== 2 || 'gesture' !== self.gesture) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
this._scaleObjectBy(self.scale);
|
||||
}
|
||||
|
||||
this.fire('touch:gesture', {target: target, e: e, self: self});
|
||||
this.fire('touch:gesture', { target: target, e: e, self: self });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
* @param self Event proxy object by Event.js
|
||||
*/
|
||||
__onDrag: function(e, self) {
|
||||
this.fire('touch:drag', {e: e, self: self});
|
||||
this.fire('touch:drag', { e: e, self: self });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
* @param self Event proxy object by Event.js
|
||||
*/
|
||||
__onOrientationChange: function(e, self) {
|
||||
this.fire('touch:orientation', {e: e, self: self});
|
||||
this.fire('touch:orientation', { e: e, self: self });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
* @param self Event proxy object by Event.js
|
||||
*/
|
||||
__onShake: function(e, self) {
|
||||
this.fire('touch:shake', {e: e, self: self});
|
||||
this.fire('touch:shake', { e: e, self: self });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -66,10 +66,9 @@
|
|||
*/
|
||||
_scaleObjectBy: function(s, by) {
|
||||
var t = this._currentTransform,
|
||||
target = t.target;
|
||||
|
||||
var lockScalingX = target.get('lockScalingX'),
|
||||
lockScalingY = target.get('lockScalingY');
|
||||
target = t.target,
|
||||
lockScalingX = target.get('lockScalingX'),
|
||||
lockScalingY = target.get('lockScalingY');
|
||||
|
||||
if (lockScalingX && lockScalingY) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -99,13 +99,11 @@
|
|||
*/
|
||||
_createGroup: function(target) {
|
||||
|
||||
var objects = this.getObjects();
|
||||
|
||||
var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target);
|
||||
|
||||
var groupObjects = isActiveLower
|
||||
? [ this._activeObject, target ]
|
||||
: [ target, this._activeObject ];
|
||||
var objects = this.getObjects(),
|
||||
isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
|
||||
groupObjects = isActiveLower
|
||||
? [ this._activeObject, target ]
|
||||
: [ target, this._activeObject ];
|
||||
|
||||
return new fabric.Group(groupObjects, {
|
||||
originX: 'center',
|
||||
|
|
|
|||
|
|
@ -129,8 +129,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
_enlivenObjects: function (objects, callback, reviver) {
|
||||
var _this = this;
|
||||
|
||||
if (objects.length === 0) {
|
||||
if (!objects || objects.length === 0) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var renderOnAddRemove = this.renderOnAddRemove;
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ fabric.Collection = {
|
|||
/**
|
||||
* Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
|
||||
* Objects should be instances of (or inherit from) fabric.Object
|
||||
* @param [...] Zero or more fabric instances
|
||||
* @param {...fabric.Object} object Zero or more fabric instances
|
||||
* @return {Self} thisArg
|
||||
*/
|
||||
add: function () {
|
||||
this._objects.push.apply(this._objects, arguments);
|
||||
for (var i = arguments.length; i--; ) {
|
||||
for (var i = 0, length = arguments.length; i < length; i++) {
|
||||
this._onObjectAdded(arguments[i]);
|
||||
}
|
||||
this.renderOnAddRemove && this.renderAll();
|
||||
|
|
@ -25,6 +25,7 @@ fabric.Collection = {
|
|||
* @param {Number} index Index to insert object at
|
||||
* @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
|
||||
* @return {Self} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
insertAt: function (object, index, nonSplicing) {
|
||||
var objects = this.getObjects();
|
||||
|
|
@ -40,22 +41,27 @@ fabric.Collection = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Removes an object from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
|
||||
* @param {Object} object Object to remove
|
||||
* Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
|
||||
* @param {...fabric.Object} object Zero or more fabric instances
|
||||
* @return {Self} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
remove: function(object) {
|
||||
remove: function() {
|
||||
var objects = this.getObjects(),
|
||||
index = objects.indexOf(object);
|
||||
index;
|
||||
|
||||
// only call onObjectRemoved if an object was actually removed
|
||||
if (index !== -1) {
|
||||
objects.splice(index, 1);
|
||||
this._onObjectRemoved(object);
|
||||
for (var i = 0, length = arguments.length; i < length; i++) {
|
||||
index = objects.indexOf(arguments[i]);
|
||||
|
||||
// only call onObjectRemoved if an object was actually removed
|
||||
if (index !== -1) {
|
||||
objects.splice(index, 1);
|
||||
this._onObjectRemoved(arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderOnAddRemove && this.renderAll();
|
||||
return object;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@
|
|||
* Initializes all the interactive behavior of IText
|
||||
*/
|
||||
initBehavior: function() {
|
||||
this.initKeyHandlers();
|
||||
this.initAddedHandler();
|
||||
this.initCursorSelectionHandlers();
|
||||
this.initDoubleClickSimulation();
|
||||
this.initHiddenTextarea();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -24,10 +23,17 @@
|
|||
setTimeout(function() {
|
||||
_this.selected = true;
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
|
||||
if (!this._hasCanvasHandlers) {
|
||||
/**
|
||||
* Initializes "added" event handler
|
||||
*/
|
||||
initAddedHandler: function() {
|
||||
this.on('added', function() {
|
||||
if (this.canvas && !this.canvas._hasITextHandlers) {
|
||||
this.canvas._hasITextHandlers = true;
|
||||
this._initCanvasHandlers();
|
||||
this._hasCanvasHandlers = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -36,25 +42,18 @@
|
|||
* @private
|
||||
*/
|
||||
_initCanvasHandlers: function() {
|
||||
var _this = this;
|
||||
|
||||
this.canvas.on('selection:cleared', function(options) {
|
||||
|
||||
// do not exit editing if event fired
|
||||
// when clicking on an object again (in editing mode)
|
||||
if (options.e && _this.canvas.containsPoint(options.e, _this)) return;
|
||||
|
||||
_this.exitEditing();
|
||||
this.canvas.on('selection:cleared', function() {
|
||||
fabric.IText.prototype.exitEditingOnOthers.call();
|
||||
});
|
||||
|
||||
this.canvas.on('mouse:up', function() {
|
||||
this.getObjects('i-text').forEach(function(obj) {
|
||||
fabric.IText.instances.forEach(function(obj) {
|
||||
obj.__isMousedown = false;
|
||||
});
|
||||
});
|
||||
|
||||
this.canvas.on('object:selected', function() {
|
||||
fabric.IText.prototype.exitEditingOnOthers.call(this);
|
||||
this.canvas.on('object:selected', function(options) {
|
||||
fabric.IText.prototype.exitEditingOnOthers.call(options.target);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -114,15 +113,23 @@
|
|||
/**
|
||||
* Initializes delayed cursor
|
||||
*/
|
||||
initDelayedCursor: function() {
|
||||
var _this = this;
|
||||
initDelayedCursor: function(restart) {
|
||||
var _this = this,
|
||||
delay = restart ? 0 : this.cursorDelay;
|
||||
|
||||
if (restart) {
|
||||
this._abortCursorAnimation = true;
|
||||
clearTimeout(this._cursorTimeout1);
|
||||
this._currentCursorOpacity = 1;
|
||||
this.canvas && this.canvas.renderAll();
|
||||
}
|
||||
if (this._cursorTimeout2) {
|
||||
clearTimeout(this._cursorTimeout2);
|
||||
}
|
||||
this._cursorTimeout2 = setTimeout(function() {
|
||||
_this._abortCursorAnimation = false;
|
||||
_this._tick();
|
||||
}, this.cursorDelay);
|
||||
}, delay);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -149,6 +156,7 @@
|
|||
selectAll: function() {
|
||||
this.selectionStart = 0;
|
||||
this.selectionEnd = this.text.length;
|
||||
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -240,8 +248,9 @@
|
|||
* @return {Number} Number of newlines in selected text
|
||||
*/
|
||||
getNumNewLinesInSelectedText: function() {
|
||||
var selectedText = this.getSelectedText();
|
||||
var numNewLines = 0;
|
||||
var selectedText = this.getSelectedText(),
|
||||
numNewLines = 0;
|
||||
|
||||
for (var i = 0, chars = selectedText.split(''), len = chars.length; i < len; i++) {
|
||||
if (chars[i] === '\n') {
|
||||
numNewLines++;
|
||||
|
|
@ -256,9 +265,9 @@
|
|||
* @param {Number} direction: 1 or -1
|
||||
*/
|
||||
searchWordBoundary: function(selectionStart, direction) {
|
||||
var index = selectionStart;
|
||||
var _char = this.text.charAt(index);
|
||||
var reNonWord = /[ \n\.,;!\?\-]/;
|
||||
var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
|
||||
_char = this.text.charAt(index),
|
||||
reNonWord = /[ \n\.,;!\?\-]/;
|
||||
|
||||
while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
|
||||
index += direction;
|
||||
|
|
@ -275,11 +284,12 @@
|
|||
* @param {Number} selectionStart Index of a character
|
||||
*/
|
||||
selectWord: function(selectionStart) {
|
||||
var newSelectionStart = this.searchWordBoundary(selectionStart, -1); /* search backwards */
|
||||
var newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
|
||||
var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
|
||||
newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
|
||||
|
||||
this.setSelectionStart(newSelectionStart);
|
||||
this.setSelectionEnd(newSelectionEnd);
|
||||
this.initDelayedCursor(true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -287,11 +297,12 @@
|
|||
* @param {Number} selectionStart Index of a character
|
||||
*/
|
||||
selectLine: function(selectionStart) {
|
||||
var newSelectionStart = this.findLineBoundaryLeft(selectionStart);
|
||||
var newSelectionEnd = this.findLineBoundaryRight(selectionStart);
|
||||
var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
|
||||
newSelectionEnd = this.findLineBoundaryRight(selectionStart);
|
||||
|
||||
this.setSelectionStart(newSelectionStart);
|
||||
this.setSelectionEnd(newSelectionEnd);
|
||||
this.initDelayedCursor(true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -306,6 +317,7 @@
|
|||
|
||||
this.isEditing = true;
|
||||
|
||||
this.initHiddenTextarea();
|
||||
this._updateTextarea();
|
||||
this._saveEditingProps();
|
||||
this._setEditingProps();
|
||||
|
|
@ -314,14 +326,17 @@
|
|||
this.canvas && this.canvas.renderAll();
|
||||
|
||||
this.fire('editing:entered');
|
||||
this.canvas && this.canvas.fire('text:editing:entered', { target: this });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
exitEditingOnOthers: function() {
|
||||
fabric.IText.instances.forEach(function(obj) {
|
||||
if (obj === this) return;
|
||||
obj.exitEditing();
|
||||
obj.selected = false;
|
||||
if (obj.isEditing) {
|
||||
obj.exitEditing();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
|
@ -349,7 +364,6 @@
|
|||
|
||||
this.hiddenTextarea.value = this.text;
|
||||
this.hiddenTextarea.selectionStart = this.selectionStart;
|
||||
this.hiddenTextarea.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -397,13 +411,15 @@
|
|||
this.selectable = true;
|
||||
|
||||
this.selectionEnd = this.selectionStart;
|
||||
this.hiddenTextarea && this.hiddenTextarea.blur();
|
||||
this.hiddenTextarea && this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
|
||||
this.hiddenTextarea = null;
|
||||
|
||||
this.abortCursorAnimation();
|
||||
this._restoreEditingProps();
|
||||
this._currentCursorOpacity = 0;
|
||||
|
||||
this.fire('editing:exited');
|
||||
this.canvas && this.canvas.fire('text:editing:exited', { target: this });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
|
@ -430,8 +446,9 @@
|
|||
|
||||
var prevIndex = this.get2DCursorLocation(i).charIndex;
|
||||
i--;
|
||||
var index = this.get2DCursorLocation(i).charIndex;
|
||||
var isNewline = index > prevIndex;
|
||||
|
||||
var index = this.get2DCursorLocation(i).charIndex,
|
||||
isNewline = index > prevIndex;
|
||||
|
||||
if (isNewline) {
|
||||
this.removeStyleObject(isNewline, i + 1);
|
||||
|
|
@ -460,10 +477,10 @@
|
|||
if (this.selectionStart === this.selectionEnd) {
|
||||
this.insertStyleObjects(_chars, isEndOfLine, this.copiedStyles);
|
||||
}
|
||||
else if (this.selectionEnd - this.selectionStart > 1) {
|
||||
// else if (this.selectionEnd - this.selectionStart > 1) {
|
||||
// TODO: replace styles properly
|
||||
console.log('replacing MORE than 1 char');
|
||||
}
|
||||
// console.log('replacing MORE than 1 char');
|
||||
// }
|
||||
|
||||
this.selectionStart += _chars.length;
|
||||
this.selectionEnd = this.selectionStart;
|
||||
|
|
@ -475,7 +492,8 @@
|
|||
}
|
||||
|
||||
this.setCoords();
|
||||
this.fire('text:changed');
|
||||
this.fire('changed');
|
||||
this.canvas && this.canvas.fire('text:changed', { target: this });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -622,7 +640,9 @@
|
|||
|
||||
var textLines = this.text.split(this._reNewline),
|
||||
textOnPreviousLine = textLines[lineIndex - 1],
|
||||
newCharIndexOnPrevLine = textOnPreviousLine.length;
|
||||
newCharIndexOnPrevLine = textOnPreviousLine
|
||||
? textOnPreviousLine.length
|
||||
: 0;
|
||||
|
||||
if (!this.styles[lineIndex - 1]) {
|
||||
this.styles[lineIndex - 1] = { };
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
// for triple click
|
||||
this.__lastLastClickTime = +new Date();
|
||||
|
||||
this.lastPointer = { };
|
||||
this.__lastPointer = { };
|
||||
|
||||
this.on('mousedown', this.onMouseDown.bind(this));
|
||||
},
|
||||
|
|
@ -32,12 +32,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
this.__lastLastClickTime = this.__lastClickTime;
|
||||
this.__lastClickTime = this.__newClickTime;
|
||||
this.__lastPointer = newPointer;
|
||||
this.__lastIsEditing = this.isEditing;
|
||||
},
|
||||
|
||||
isDoubleClick: function(newPointer) {
|
||||
return this.__newClickTime - this.__lastClickTime < 500 &&
|
||||
this.__lastPointer.x === newPointer.x &&
|
||||
this.__lastPointer.y === newPointer.y;
|
||||
this.__lastPointer.y === newPointer.y && this.__lastIsEditing;
|
||||
},
|
||||
|
||||
isTripleClick: function(newPointer) {
|
||||
|
|
@ -94,9 +95,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
|
||||
}
|
||||
|
||||
if (this.isEditing) {
|
||||
if (this.selected) {
|
||||
this.setCursorByClick(options.e);
|
||||
}
|
||||
|
||||
if (this.isEditing) {
|
||||
this.__selectionStartOnMouseDown = this.selectionStart;
|
||||
this.initDelayedCursor(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -137,12 +142,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
initMouseupHandler: function() {
|
||||
this.on('mouseup', function(options) {
|
||||
this.__isMousedown = false;
|
||||
|
||||
if (this._isObjectMoved(options.e)) return;
|
||||
|
||||
if (this.selected) {
|
||||
this.enterEditing();
|
||||
this.initDelayedCursor(true);
|
||||
}
|
||||
this.selected = true;
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -204,10 +210,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
|
||||
height += this._getHeightOfLine(this.ctx, i) * this.scaleY;
|
||||
|
||||
var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines);
|
||||
var lineLeftOffset = this._getLineLeftOffset(widthOfLine);
|
||||
var widthOfLine = this._getWidthOfLine(this.ctx, i, textLines),
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfLine);
|
||||
|
||||
width = lineLeftOffset;
|
||||
width = lineLeftOffset * this.scaleX;
|
||||
|
||||
if (this.flipX) {
|
||||
// when oject is horizontally flipped we reverse chars
|
||||
|
|
@ -230,6 +236,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
return this._getNewSelectionStartFromOffset(
|
||||
mouseOffset, prevWidth, width, charIndex + i, jlen);
|
||||
}
|
||||
|
||||
if (mouseOffset.y < height) {
|
||||
return this._getNewSelectionStartFromOffset(
|
||||
mouseOffset, prevWidth, width, charIndex + i, jlen, j);
|
||||
}
|
||||
}
|
||||
|
||||
// clicked somewhere after all chars, so set at the end
|
||||
|
|
@ -241,7 +252,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
|
||||
_getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen, j) {
|
||||
|
||||
var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
|
||||
distanceBtwNextCharAndCursor = width - mouseOffset.x,
|
||||
|
|
@ -257,6 +268,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
newSelectionStart = this.text.length;
|
||||
}
|
||||
|
||||
if (j === jlen) {
|
||||
newSelectionStart--;
|
||||
}
|
||||
|
||||
return newSelectionStart;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
|
||||
|
||||
/**
|
||||
* Initializes key handlers
|
||||
*/
|
||||
initKeyHandlers: function() {
|
||||
fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this));
|
||||
fabric.util.addListener(fabric.document, 'keypress', this.onKeyPress.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes hidden textarea (needed to bring up keyboard in iOS)
|
||||
*/
|
||||
|
|
@ -18,6 +10,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
this.hiddenTextarea.style.cssText = 'position: absolute; top: 0; left: -9999px';
|
||||
|
||||
fabric.document.body.appendChild(this.hiddenTextarea);
|
||||
|
||||
fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
|
||||
fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
|
||||
|
||||
if (!this._clickHandlerInitialized && this.canvas) {
|
||||
fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
|
||||
this._clickHandlerInitialized = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -43,6 +43,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
88: 'cut'
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
// No need to trigger click event here, focus is enough to have the keyboard appear on Android
|
||||
this.hiddenTextarea && this.hiddenTextarea.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles keyup event
|
||||
* @param {Event} e Event object
|
||||
|
|
@ -149,8 +154,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
|
||||
|
||||
var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
|
||||
var lineIndex = cursorLocation.lineIndex;
|
||||
var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
|
||||
lineIndex = cursorLocation.lineIndex;
|
||||
|
||||
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
|
||||
_char = textOnSameLineBeforeCursor[i];
|
||||
|
|
@ -168,17 +173,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
*/
|
||||
_getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
|
||||
|
||||
var lineIndex = cursorLocation.lineIndex + 1;
|
||||
var widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
|
||||
var lineLeftOffset = this._getLineLeftOffset(widthOfNextLine);
|
||||
var widthOfCharsOnNextLine = lineLeftOffset;
|
||||
var indexOnNextLine = 0;
|
||||
var foundMatch;
|
||||
var lineIndex = cursorLocation.lineIndex + 1,
|
||||
widthOfNextLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
|
||||
widthOfCharsOnNextLine = lineLeftOffset,
|
||||
indexOnNextLine = 0,
|
||||
foundMatch;
|
||||
|
||||
for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
|
||||
|
||||
var _char = textOnNextLine[j];
|
||||
var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
|
||||
var _char = textOnNextLine[j],
|
||||
widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
|
||||
|
||||
widthOfCharsOnNextLine += widthOfChar;
|
||||
|
||||
|
|
@ -186,10 +191,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
|
||||
foundMatch = true;
|
||||
|
||||
var leftEdge = widthOfCharsOnNextLine - widthOfChar;
|
||||
var rightEdge = widthOfCharsOnNextLine;
|
||||
var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
var leftEdge = widthOfCharsOnNextLine - widthOfChar,
|
||||
rightEdge = widthOfCharsOnNextLine,
|
||||
offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
|
||||
offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
|
||||
indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
|
||||
|
||||
|
|
@ -277,13 +282,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
textOnPreviousLine = (textBeforeCursor.match(/\n?(.*)\n.*$/) || {})[1] || '',
|
||||
textLines = this.text.split(this._reNewline),
|
||||
_char,
|
||||
lineLeftOffset;
|
||||
|
||||
var widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines);
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
|
||||
|
||||
var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
|
||||
var lineIndex = cursorLocation.lineIndex;
|
||||
widthOfSameLineBeforeCursor = this._getWidthOfLine(this.ctx, cursorLocation.lineIndex, textLines),
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
|
||||
widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
|
||||
lineIndex = cursorLocation.lineIndex;
|
||||
|
||||
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
|
||||
_char = textOnSameLineBeforeCursor[i];
|
||||
|
|
@ -301,17 +303,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
*/
|
||||
_getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor, textLines) {
|
||||
|
||||
var lineIndex = cursorLocation.lineIndex - 1;
|
||||
var widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines);
|
||||
var lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine);
|
||||
var widthOfCharsOnPreviousLine = lineLeftOffset;
|
||||
var indexOnPrevLine = 0;
|
||||
var foundMatch;
|
||||
var lineIndex = cursorLocation.lineIndex - 1,
|
||||
widthOfPreviousLine = this._getWidthOfLine(this.ctx, lineIndex, textLines),
|
||||
lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
|
||||
widthOfCharsOnPreviousLine = lineLeftOffset,
|
||||
indexOnPrevLine = 0,
|
||||
foundMatch;
|
||||
|
||||
for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
|
||||
|
||||
var _char = textOnPreviousLine[j];
|
||||
var widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
|
||||
var _char = textOnPreviousLine[j],
|
||||
widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
|
||||
|
||||
widthOfCharsOnPreviousLine += widthOfChar;
|
||||
|
||||
|
|
@ -319,10 +321,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
|
||||
foundMatch = true;
|
||||
|
||||
var leftEdge = widthOfCharsOnPreviousLine - widthOfChar;
|
||||
var rightEdge = widthOfCharsOnPreviousLine;
|
||||
var offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
var offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
|
||||
rightEdge = widthOfCharsOnPreviousLine,
|
||||
offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
|
||||
offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
|
||||
|
||||
indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
|
||||
|
||||
|
|
@ -572,7 +574,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
}
|
||||
|
||||
this.setCoords();
|
||||
this.fire('text:changed');
|
||||
this.fire('changed');
|
||||
this.canvas && this.canvas.fire('text:changed', { target: this });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -596,7 +599,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
this.selectionStart = leftWordBoundary;
|
||||
}
|
||||
else {
|
||||
var isBeginningOfLine = this.text.slice(this.selectionStart-1, this.selectionStart) === '\n';
|
||||
var isBeginningOfLine = this.text.slice(this.selectionStart - 1, this.selectionStart) === '\n';
|
||||
this.removeStyleObject(isBeginningOfLine);
|
||||
|
||||
this.selectionStart--;
|
||||
|
|
|
|||
|
|
@ -8,32 +8,32 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
getSvgStyles: function() {
|
||||
|
||||
var fill = this.fill
|
||||
? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
|
||||
: 'none';
|
||||
? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
|
||||
: 'none',
|
||||
|
||||
var stroke = this.stroke
|
||||
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
|
||||
: 'none';
|
||||
stroke = this.stroke
|
||||
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
|
||||
: 'none',
|
||||
|
||||
var strokeWidth = this.strokeWidth ? this.strokeWidth : '0';
|
||||
var strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '';
|
||||
var strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt';
|
||||
var strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter';
|
||||
var strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4';
|
||||
var opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1';
|
||||
strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
|
||||
strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
|
||||
strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
|
||||
strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
|
||||
strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
|
||||
opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
|
||||
|
||||
var visibility = this.visible ? '' : " visibility: hidden;";
|
||||
var filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
|
||||
visibility = this.visible ? '' : ' visibility: hidden;',
|
||||
filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
|
||||
|
||||
return [
|
||||
"stroke: ", stroke, "; ",
|
||||
"stroke-width: ", strokeWidth, "; ",
|
||||
"stroke-dasharray: ", strokeDashArray, "; ",
|
||||
"stroke-linecap: ", strokeLineCap, "; ",
|
||||
"stroke-linejoin: ", strokeLineJoin, "; ",
|
||||
"stroke-miterlimit: ", strokeMiterLimit, "; ",
|
||||
"fill: ", fill, "; ",
|
||||
"opacity: ", opacity, ";",
|
||||
'stroke: ', stroke, '; ',
|
||||
'stroke-width: ', strokeWidth, '; ',
|
||||
'stroke-dasharray: ', strokeDashArray, '; ',
|
||||
'stroke-linecap: ', strokeLineCap, '; ',
|
||||
'stroke-linejoin: ', strokeLineJoin, '; ',
|
||||
'stroke-miterlimit: ', strokeMiterLimit, '; ',
|
||||
'fill: ', fill, '; ',
|
||||
'opacity: ', opacity, ';',
|
||||
filter,
|
||||
visibility
|
||||
].join('');
|
||||
|
|
@ -44,34 +44,37 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {String}
|
||||
*/
|
||||
getSvgTransform: function() {
|
||||
var toFixed = fabric.util.toFixed;
|
||||
var angle = this.getAngle();
|
||||
var center = this.getCenterPoint();
|
||||
var toFixed = fabric.util.toFixed,
|
||||
angle = this.getAngle(),
|
||||
center = this.getCenterPoint(),
|
||||
|
||||
var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
|
||||
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
|
||||
|
||||
var translatePart = "translate(" +
|
||||
translatePart = 'translate(' +
|
||||
toFixed(center.x, NUM_FRACTION_DIGITS) +
|
||||
" " +
|
||||
' ' +
|
||||
toFixed(center.y, NUM_FRACTION_DIGITS) +
|
||||
")";
|
||||
')',
|
||||
|
||||
var anglePart = angle !== 0
|
||||
? (" rotate(" + toFixed(angle, NUM_FRACTION_DIGITS) + ")")
|
||||
: '';
|
||||
anglePart = angle !== 0
|
||||
? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
|
||||
: '',
|
||||
|
||||
var scalePart = (this.scaleX === 1 && this.scaleY === 1)
|
||||
? '' :
|
||||
(" scale(" +
|
||||
toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
|
||||
" " +
|
||||
toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
|
||||
")");
|
||||
scalePart = (this.scaleX === 1 && this.scaleY === 1)
|
||||
? '' :
|
||||
(' scale(' +
|
||||
toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
|
||||
' ' +
|
||||
toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
|
||||
')'),
|
||||
|
||||
var flipXPart = this.flipX ? "matrix(-1 0 0 1 0 0) " : "";
|
||||
var flipYPart = this.flipY ? "matrix(1 0 0 -1 0 0)" : "";
|
||||
flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
|
||||
|
||||
return [ translatePart, anglePart, scalePart, flipXPart, flipYPart ].join('');
|
||||
flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
|
||||
|
||||
return [
|
||||
translatePart, anglePart, scalePart, flipXPart, flipYPart
|
||||
].join('');
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,13 +22,12 @@
|
|||
tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
|
||||
tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
|
||||
bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
|
||||
br = new fabric.Point(oCoords.br.x, oCoords.br.y);
|
||||
|
||||
var intersection = fabric.Intersection.intersectPolygonRectangle(
|
||||
[tl, tr, br, bl],
|
||||
pointTL,
|
||||
pointBR
|
||||
);
|
||||
br = new fabric.Point(oCoords.br.x, oCoords.br.y),
|
||||
intersection = fabric.Intersection.intersectPolygonRectangle(
|
||||
[tl, tr, br, bl],
|
||||
pointTL,
|
||||
pointBR
|
||||
);
|
||||
return intersection.status === 'Intersection';
|
||||
},
|
||||
|
||||
|
|
@ -48,12 +47,11 @@
|
|||
};
|
||||
}
|
||||
var thisCoords = getCoords(this.oCoords),
|
||||
otherCoords = getCoords(other.oCoords);
|
||||
|
||||
var intersection = fabric.Intersection.intersectPolygonPolygon(
|
||||
[thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
|
||||
[otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
|
||||
);
|
||||
otherCoords = getCoords(other.oCoords),
|
||||
intersection = fabric.Intersection.intersectPolygonPolygon(
|
||||
[thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
|
||||
[otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
|
||||
);
|
||||
|
||||
return intersection.status === 'Intersection';
|
||||
},
|
||||
|
|
@ -81,10 +79,10 @@
|
|||
var boundingRect = this.getBoundingRect();
|
||||
|
||||
return (
|
||||
boundingRect.left > pointTL.x &&
|
||||
boundingRect.left + boundingRect.width < pointBR.x &&
|
||||
boundingRect.top > pointTL.y &&
|
||||
boundingRect.top + boundingRect.height < pointBR.y
|
||||
boundingRect.left >= pointTL.x &&
|
||||
boundingRect.left + boundingRect.width <= pointBR.x &&
|
||||
boundingRect.top >= pointTL.y &&
|
||||
boundingRect.top + boundingRect.height <= pointBR.y
|
||||
);
|
||||
},
|
||||
|
||||
|
|
@ -158,7 +156,7 @@
|
|||
else {
|
||||
b1 = 0;
|
||||
b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
|
||||
a1 = point.y- b1 * point.x;
|
||||
a1 = point.y - b1 * point.x;
|
||||
a2 = iLine.o.y - b2 * iLine.o.x;
|
||||
|
||||
xi = - (a1 - a2) / (b1 - b2);
|
||||
|
|
@ -201,15 +199,15 @@
|
|||
getBoundingRect: function() {
|
||||
this.oCoords || this.setCoords();
|
||||
|
||||
var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x];
|
||||
var minX = fabric.util.array.min(xCoords);
|
||||
var maxX = fabric.util.array.max(xCoords);
|
||||
var width = Math.abs(minX - maxX);
|
||||
var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
|
||||
minX = fabric.util.array.min(xCoords),
|
||||
maxX = fabric.util.array.max(xCoords),
|
||||
width = Math.abs(minX - maxX),
|
||||
|
||||
var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y];
|
||||
var minY = fabric.util.array.min(yCoords);
|
||||
var maxY = fabric.util.array.max(yCoords);
|
||||
var height = Math.abs(minY - maxY);
|
||||
yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
|
||||
minY = fabric.util.array.min(yCoords),
|
||||
maxY = fabric.util.array.max(yCoords),
|
||||
height = Math.abs(minY - maxY);
|
||||
|
||||
return {
|
||||
left: minX,
|
||||
|
|
@ -243,12 +241,13 @@
|
|||
*/
|
||||
_constrainScale: function(value) {
|
||||
if (Math.abs(value) < this.minScaleLimit) {
|
||||
if (value < 0)
|
||||
if (value < 0) {
|
||||
return -this.minScaleLimit;
|
||||
else
|
||||
}
|
||||
else {
|
||||
return this.minScaleLimit;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
|
|
@ -307,9 +306,7 @@
|
|||
var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
|
||||
padding = this.padding,
|
||||
theta = degreesToRadians(this.angle),
|
||||
vpt;
|
||||
// TODO: ideally we should never setCoords an object which lacks a canvas
|
||||
vpt = this.canvas ? this.canvas.viewportTransform : [1, 0, 0, 1, 0, 0];
|
||||
vpt = this.getViewportTransform();
|
||||
|
||||
var f = function (p) {
|
||||
return fabric.util.transformPoint(p, vpt);
|
||||
|
|
@ -324,13 +321,13 @@
|
|||
}
|
||||
|
||||
var _hypotenuse = Math.sqrt(
|
||||
Math.pow(this.currentWidth / 2, 2) +
|
||||
Math.pow(this.currentHeight / 2, 2));
|
||||
Math.pow(this.currentWidth / 2, 2) +
|
||||
Math.pow(this.currentHeight / 2, 2)),
|
||||
|
||||
var _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0);
|
||||
_angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
|
||||
|
||||
// offset added for rotate and scale actions
|
||||
var offsetX = Math.cos(_angle + theta) * _hypotenuse,
|
||||
// offset added for rotate and scale actions
|
||||
offsetX = Math.cos(_angle + theta) * _hypotenuse,
|
||||
offsetY = Math.sin(_angle + theta) * _hypotenuse,
|
||||
sinTh = Math.sin(theta),
|
||||
cosTh = Math.cos(theta),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
(function(){
|
||||
|
||||
var getPointer = fabric.util.getPointer,
|
||||
degreesToRadians = fabric.util.degreesToRadians,
|
||||
var degreesToRadians = fabric.util.degreesToRadians,
|
||||
isVML = typeof G_vmlCanvasManager !== 'undefined';
|
||||
|
||||
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
|
||||
|
|
@ -15,15 +14,13 @@
|
|||
/**
|
||||
* Determines which corner has been clicked
|
||||
* @private
|
||||
* @param {Event} e Event object
|
||||
* @param {Object} offset Canvas offset
|
||||
* @param {Object} pointer The pointer indicating the mouse position
|
||||
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
|
||||
*/
|
||||
_findTargetCorner: function(e, offset) {
|
||||
_findTargetCorner: function(pointer) {
|
||||
if (!this.hasControls || !this.active) return false;
|
||||
|
||||
var pointer = this.canvas.getPointer(e, true),
|
||||
ex = pointer.x,
|
||||
var ex = pointer.x,
|
||||
ey = pointer.y,
|
||||
xPoints,
|
||||
lines;
|
||||
|
|
@ -38,7 +35,8 @@
|
|||
continue;
|
||||
}
|
||||
|
||||
if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
|
||||
if (this.get('lockUniScaling') &&
|
||||
(i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +56,7 @@
|
|||
// canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
|
||||
// canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
|
||||
|
||||
xPoints = this._findCrossPoints({x: ex, y: ey}, lines);
|
||||
xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
|
||||
if (xPoints !== 0 && xPoints % 2 === 1) {
|
||||
this.__corner = i;
|
||||
return i;
|
||||
|
|
@ -278,7 +276,7 @@
|
|||
|
||||
ctx.lineWidth = 1 / this.borderScaleFactor;
|
||||
|
||||
var vpt = this.canvas.viewportTransform,
|
||||
var vpt = this.getViewportTransform(),
|
||||
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, true),
|
||||
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), vpt, true),
|
||||
w = wh.x,
|
||||
|
|
@ -330,7 +328,7 @@
|
|||
var size = this.cornerSize,
|
||||
size2 = size / 2,
|
||||
strokeWidth2 = ~~(this.strokeWidth / 2), // half strokeWidth rounded down
|
||||
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.canvas.viewportTransform, true),
|
||||
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.getViewportTransform(), true),
|
||||
width = wh.x,
|
||||
height = wh.y,
|
||||
left = -(width / 2),
|
||||
|
|
@ -360,7 +358,7 @@
|
|||
top - scaleOffset - strokeWidth2 - padding);
|
||||
|
||||
// bottom-left
|
||||
this._drawControl('tr', ctx, methodName,
|
||||
this._drawControl('bl', ctx, methodName,
|
||||
left - scaleOffset - strokeWidth2 - padding,
|
||||
top + height + scaleOffsetSize + strokeWidth2 + padding);
|
||||
|
||||
|
|
@ -382,7 +380,7 @@
|
|||
top + height + scaleOffsetSize + strokeWidth2 + padding);
|
||||
|
||||
// middle-right
|
||||
this._drawControl('mb', ctx, methodName,
|
||||
this._drawControl('mr', ctx, methodName,
|
||||
left + width + scaleOffsetSize + strokeWidth2 + padding,
|
||||
top + height/2 - scaleOffset);
|
||||
|
||||
|
|
@ -471,15 +469,15 @@
|
|||
_getControlsVisibility: function() {
|
||||
if (!this._controlsVisibility) {
|
||||
this._controlsVisibility = {
|
||||
tl: true,
|
||||
tr: true,
|
||||
br: true,
|
||||
bl: true,
|
||||
ml: true,
|
||||
mt: true,
|
||||
mr: true,
|
||||
mb: true,
|
||||
mtr: true
|
||||
tl: true,
|
||||
tr: true,
|
||||
br: true,
|
||||
bl: true,
|
||||
ml: true,
|
||||
mt: true,
|
||||
mr: true,
|
||||
mb: true,
|
||||
mtr: true
|
||||
};
|
||||
}
|
||||
return this._controlsVisibility;
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@
|
|||
cy = point.y,
|
||||
strokeWidth = this.stroke ? this.strokeWidth : 0;
|
||||
|
||||
if (originX === "left") {
|
||||
if (originX === 'left') {
|
||||
cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
else if (originX === "right") {
|
||||
else if (originX === 'right') {
|
||||
cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
|
||||
if (originY === "top") {
|
||||
if (originY === 'top') {
|
||||
cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
else if (originY === "bottom") {
|
||||
else if (originY === 'bottom') {
|
||||
cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
|
||||
|
|
@ -47,16 +47,16 @@
|
|||
strokeWidth = this.stroke ? this.strokeWidth : 0;
|
||||
|
||||
// Get the point coordinates
|
||||
if (originX === "left") {
|
||||
if (originX === 'left') {
|
||||
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
else if (originX === "right") {
|
||||
else if (originX === 'right') {
|
||||
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
if (originY === "top") {
|
||||
if (originY === 'top') {
|
||||
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
else if (originY === "bottom") {
|
||||
else if (originY === 'bottom') {
|
||||
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
|
||||
|
|
@ -106,20 +106,20 @@
|
|||
x, y;
|
||||
|
||||
if (originX && originY) {
|
||||
if (originX === "left") {
|
||||
if (originX === 'left') {
|
||||
x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
else if (originX === "right") {
|
||||
else if (originX === 'right') {
|
||||
x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
|
||||
}
|
||||
else {
|
||||
x = center.x;
|
||||
}
|
||||
|
||||
if (originY === "top") {
|
||||
if (originY === 'top') {
|
||||
y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
else if (originY === "bottom") {
|
||||
else if (originY === 'bottom') {
|
||||
y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
|
||||
}
|
||||
else {
|
||||
|
|
@ -152,8 +152,8 @@
|
|||
* @return {void}
|
||||
*/
|
||||
setPositionByOrigin: function(pos, originX, originY) {
|
||||
var center = this.translateToCenterPoint(pos, originX, originY);
|
||||
var position = this.translateToOriginPoint(center, this.originX, this.originY);
|
||||
var center = this.translateToCenterPoint(pos, originX, originY),
|
||||
position = this.translateToOriginPoint(center, this.originX, this.originY);
|
||||
|
||||
this.set('left', position.x);
|
||||
this.set('top', position.y);
|
||||
|
|
@ -163,13 +163,13 @@
|
|||
* @param {String} to One of 'left', 'center', 'right'
|
||||
*/
|
||||
adjustPosition: function(to) {
|
||||
var angle = degreesToRadians(this.angle);
|
||||
var hypotHalf = this.getWidth() / 2;
|
||||
var xHalf = Math.cos(angle) * hypotHalf;
|
||||
var yHalf = Math.sin(angle) * hypotHalf;
|
||||
var hypotFull = this.getWidth();
|
||||
var xFull = Math.cos(angle) * hypotFull;
|
||||
var yFull = Math.sin(angle) * hypotFull;
|
||||
var angle = degreesToRadians(this.angle),
|
||||
hypotHalf = this.getWidth() / 2,
|
||||
xHalf = Math.cos(angle) * hypotHalf,
|
||||
yHalf = Math.sin(angle) * hypotHalf,
|
||||
hypotFull = this.getWidth(),
|
||||
xFull = Math.cos(angle) * hypotFull,
|
||||
yFull = Math.sin(angle) * hypotFull;
|
||||
|
||||
if (this.originX === 'center' && to === 'left' ||
|
||||
this.originX === 'right' && to === 'center') {
|
||||
|
|
@ -198,6 +198,45 @@
|
|||
this.originX = to;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets the origin/position of the object to it's center point
|
||||
* @return {void}
|
||||
*/
|
||||
_setOriginToCenter: function() {
|
||||
this._originalOriginX = this.originX;
|
||||
this._originalOriginY = this.originY;
|
||||
|
||||
var center = this.getCenterPoint();
|
||||
|
||||
this.originX = 'center';
|
||||
this.originY = 'center';
|
||||
|
||||
this.left = center.x;
|
||||
this.top = center.y;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Resets the origin/position of the object to it's original origin
|
||||
* @return {void}
|
||||
*/
|
||||
_resetOrigin: function() {
|
||||
var originPoint = this.translateToOriginPoint(
|
||||
this.getCenterPoint(),
|
||||
this._originalOriginX,
|
||||
this._originalOriginY);
|
||||
|
||||
this.originX = this._originalOriginX;
|
||||
this.originY = this._originalOriginY;
|
||||
|
||||
this.left = originPoint.x;
|
||||
this.top = originPoint.y;
|
||||
|
||||
this._originalOriginX = null;
|
||||
this._originalOriginY = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
_getAngleValueForStraighten: function() {
|
||||
var angle = this.getAngle() % 360;
|
||||
if (angle > 0) {
|
||||
return Math.round((angle-1)/90) * 90;
|
||||
return Math.round((angle - 1) / 90) * 90;
|
||||
}
|
||||
return Math.round(angle/90) * 90;
|
||||
return Math.round(angle / 90) * 90;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
70
src/node.js
70
src/node.js
|
|
@ -4,7 +4,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var DOMParser = new require('xmldom').DOMParser,
|
||||
var DOMParser = require('xmldom').DOMParser,
|
||||
URL = require('url'),
|
||||
HTTP = require('http'),
|
||||
HTTPS = require('https'),
|
||||
|
|
@ -22,27 +22,26 @@
|
|||
}
|
||||
|
||||
// assign request handler based on protocol
|
||||
var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP;
|
||||
|
||||
var req = reqHandler.request({
|
||||
hostname: oURL.hostname,
|
||||
port: oURL.port,
|
||||
path: oURL.path,
|
||||
method: 'GET'
|
||||
}, function(response){
|
||||
var body = "";
|
||||
if (encoding) {
|
||||
response.setEncoding(encoding);
|
||||
}
|
||||
response.on('end', function () {
|
||||
callback(body);
|
||||
});
|
||||
response.on('data', function (chunk) {
|
||||
if (response.statusCode === 200) {
|
||||
body += chunk;
|
||||
}
|
||||
});
|
||||
});
|
||||
var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP,
|
||||
req = reqHandler.request({
|
||||
hostname: oURL.hostname,
|
||||
port: oURL.port,
|
||||
path: oURL.path,
|
||||
method: 'GET'
|
||||
}, function(response) {
|
||||
var body = '';
|
||||
if (encoding) {
|
||||
response.setEncoding(encoding);
|
||||
}
|
||||
response.on('end', function () {
|
||||
callback(body);
|
||||
});
|
||||
response.on('data', function (chunk) {
|
||||
if (response.statusCode === 200) {
|
||||
body += chunk;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function(err) {
|
||||
if (err.errno === process.ECONNREFUSED) {
|
||||
|
|
@ -57,32 +56,33 @@
|
|||
}
|
||||
|
||||
/** @private */
|
||||
function request_fs(path, callback){
|
||||
function requestFs(path, callback){
|
||||
var fs = require('fs');
|
||||
fs.readFile(path, function (err, data) {
|
||||
if (err) {
|
||||
fabric.log(err);
|
||||
throw err;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
callback(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fabric.util.loadImage = function(url, callback, context) {
|
||||
var createImageAndCallBack = function(data){
|
||||
function createImageAndCallBack(data) {
|
||||
img.src = new Buffer(data, 'binary');
|
||||
// preserving original url, which seems to be lost in node-canvas
|
||||
img._src = url;
|
||||
callback && callback.call(context, img);
|
||||
};
|
||||
}
|
||||
var img = new Image();
|
||||
if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
|
||||
img.src = img._src = url;
|
||||
callback && callback.call(context, img);
|
||||
}
|
||||
else if (url && url.indexOf('http') !== 0) {
|
||||
request_fs(url, createImageAndCallBack);
|
||||
requestFs(url, createImageAndCallBack);
|
||||
}
|
||||
else if (url) {
|
||||
request(url, 'binary', createImageAndCallBack);
|
||||
|
|
@ -95,8 +95,8 @@
|
|||
fabric.loadSVGFromURL = function(url, callback, reviver) {
|
||||
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
|
||||
if (url.indexOf('http') !== 0) {
|
||||
request_fs(url, function(body) {
|
||||
fabric.loadSVGFromString(body, callback, reviver);
|
||||
requestFs(url, function(body) {
|
||||
fabric.loadSVGFromString(body.toString(), callback, reviver);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
|
@ -136,12 +136,15 @@
|
|||
* Only available when running fabric on node.js
|
||||
* @param width Canvas width
|
||||
* @param height Canvas height
|
||||
* @param {Object} options to pass to FabricCanvas.
|
||||
* @param {Object} options to pass to NodeCanvas.
|
||||
* @return {Object} wrapped canvas instance
|
||||
*/
|
||||
fabric.createCanvasForNode = function(width, height) {
|
||||
fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
|
||||
nodeCanvasOptions = nodeCanvasOptions || options;
|
||||
|
||||
var canvasEl = fabric.document.createElement('canvas'),
|
||||
nodeCanvas = new Canvas(width || 600, height || 600);
|
||||
nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
|
||||
|
||||
// jsdom doesn't create style on canvas element, so here be temp. workaround
|
||||
canvasEl.style = { };
|
||||
|
|
@ -149,8 +152,9 @@
|
|||
canvasEl.width = nodeCanvas.width;
|
||||
canvasEl.height = nodeCanvas.height;
|
||||
|
||||
var FabricCanvas = fabric.Canvas || fabric.StaticCanvas;
|
||||
var fabricCanvas = new FabricCanvas(canvasEl);
|
||||
var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
|
||||
fabricCanvas = new FabricCanvas(canvasEl, options);
|
||||
|
||||
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
|
||||
fabricCanvas.nodeCanvas = nodeCanvas;
|
||||
fabricCanvas.Font = Canvas.Font;
|
||||
|
|
|
|||
207
src/parser.js
207
src/parser.js
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @name fabric
|
||||
|
|
@ -12,34 +12,37 @@
|
|||
capitalize = fabric.util.string.capitalize,
|
||||
clone = fabric.util.object.clone,
|
||||
toFixed = fabric.util.toFixed,
|
||||
multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
|
||||
multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
|
||||
|
||||
var attributesMap = {
|
||||
'fill-opacity': 'fillOpacity',
|
||||
'fill-rule': 'fillRule',
|
||||
'font-family': 'fontFamily',
|
||||
'font-size': 'fontSize',
|
||||
'font-style': 'fontStyle',
|
||||
'font-weight': 'fontWeight',
|
||||
'cx': 'left',
|
||||
'x': 'left',
|
||||
'r': 'radius',
|
||||
'stroke-dasharray': 'strokeDashArray',
|
||||
'stroke-linecap': 'strokeLineCap',
|
||||
'stroke-linejoin': 'strokeLineJoin',
|
||||
'stroke-miterlimit':'strokeMiterLimit',
|
||||
'stroke-opacity': 'strokeOpacity',
|
||||
'stroke-width': 'strokeWidth',
|
||||
'text-decoration': 'textDecoration',
|
||||
'cy': 'top',
|
||||
'y': 'top',
|
||||
'transform': 'transformMatrix'
|
||||
};
|
||||
attributesMap = {
|
||||
cx: 'left',
|
||||
x: 'left',
|
||||
r: 'radius',
|
||||
cy: 'top',
|
||||
y: 'top',
|
||||
display: 'visible',
|
||||
visibility: 'visible',
|
||||
transform: 'transformMatrix',
|
||||
'fill-opacity': 'fillOpacity',
|
||||
'fill-rule': 'fillRule',
|
||||
'font-family': 'fontFamily',
|
||||
'font-size': 'fontSize',
|
||||
'font-style': 'fontStyle',
|
||||
'font-weight': 'fontWeight',
|
||||
'stroke-dasharray': 'strokeDashArray',
|
||||
'stroke-linecap': 'strokeLineCap',
|
||||
'stroke-linejoin': 'strokeLineJoin',
|
||||
'stroke-miterlimit': 'strokeMiterLimit',
|
||||
'stroke-opacity': 'strokeOpacity',
|
||||
'stroke-width': 'strokeWidth',
|
||||
'text-decoration': 'textDecoration',
|
||||
'text-anchor': 'originX'
|
||||
},
|
||||
|
||||
var colorAttributes = {
|
||||
'stroke': 'strokeOpacity',
|
||||
'fill': 'fillOpacity'
|
||||
};
|
||||
colorAttributes = {
|
||||
stroke: 'strokeOpacity',
|
||||
fill: 'fillOpacity'
|
||||
};
|
||||
|
||||
function normalizeAttr(attr) {
|
||||
// transform attribute names
|
||||
|
|
@ -70,6 +73,16 @@
|
|||
value = fabric.parseTransformAttribute(value);
|
||||
}
|
||||
}
|
||||
else if (attr === 'visible') {
|
||||
value = (value === 'none' || value === 'hidden') ? false : true;
|
||||
// display=none on parent element always takes precedence over child element
|
||||
if (parentAttributes.visible === false) {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
else if (attr === 'originX' /* text-anchor */) {
|
||||
value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
|
||||
}
|
||||
|
||||
isArray = Object.prototype.toString.call(value) === '[object Array]';
|
||||
|
||||
|
|
@ -150,30 +163,30 @@
|
|||
],
|
||||
|
||||
// == begin transform regexp
|
||||
number = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)',
|
||||
number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
|
||||
comma_wsp = '(?:\\s+,?\\s*|,\\s*)',
|
||||
commaWsp = '(?:\\s+,?\\s*|,\\s*)',
|
||||
|
||||
skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
|
||||
|
||||
skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
|
||||
|
||||
rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
|
||||
comma_wsp + '(' + number + ')' +
|
||||
comma_wsp + '(' + number + '))?\\s*\\))',
|
||||
commaWsp + '(' + number + ')' +
|
||||
commaWsp + '(' + number + '))?\\s*\\))',
|
||||
|
||||
scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
|
||||
comma_wsp + '(' + number + '))?\\s*\\))',
|
||||
commaWsp + '(' + number + '))?\\s*\\))',
|
||||
|
||||
translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
|
||||
comma_wsp + '(' + number + '))?\\s*\\))',
|
||||
commaWsp + '(' + number + '))?\\s*\\))',
|
||||
|
||||
matrix = '(?:(matrix)\\s*\\(\\s*' +
|
||||
'(' + number + ')' + comma_wsp +
|
||||
'(' + number + ')' + comma_wsp +
|
||||
'(' + number + ')' + comma_wsp +
|
||||
'(' + number + ')' + comma_wsp +
|
||||
'(' + number + ')' + comma_wsp +
|
||||
'(' + number + ')' + commaWsp +
|
||||
'(' + number + ')' + commaWsp +
|
||||
'(' + number + ')' + commaWsp +
|
||||
'(' + number + ')' + commaWsp +
|
||||
'(' + number + ')' + commaWsp +
|
||||
'(' + number + ')' +
|
||||
'\\s*\\))',
|
||||
|
||||
|
|
@ -186,12 +199,12 @@
|
|||
skewY +
|
||||
')',
|
||||
|
||||
transforms = '(?:' + transform + '(?:' + comma_wsp + transform + ')*' + ')',
|
||||
transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
|
||||
|
||||
transform_list = '^\\s*(?:' + transforms + '?)\\s*$',
|
||||
transformList = '^\\s*(?:' + transforms + '?)\\s*$',
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
|
||||
reTransformList = new RegExp(transform_list),
|
||||
reTransformList = new RegExp(transformList),
|
||||
// == end transform regexp
|
||||
|
||||
reTransform = new RegExp(transform, 'g');
|
||||
|
|
@ -199,8 +212,8 @@
|
|||
return function(attributeValue) {
|
||||
|
||||
// start with identity matrix
|
||||
var matrix = iMatrix.concat();
|
||||
var matrices = [ ];
|
||||
var matrix = iMatrix.concat(),
|
||||
matrices = [ ];
|
||||
|
||||
// return if no argument was given or
|
||||
// an argument does not match transform attribute regexp
|
||||
|
|
@ -216,11 +229,12 @@
|
|||
operation = m[1],
|
||||
args = m.slice(2).map(parseFloat);
|
||||
|
||||
switch(operation) {
|
||||
switch (operation) {
|
||||
case 'translate':
|
||||
translateMatrix(matrix, args);
|
||||
break;
|
||||
case 'rotate':
|
||||
args[0] = fabric.util.degreesToRadians(args[0]);
|
||||
rotateMatrix(matrix, args);
|
||||
break;
|
||||
case 'scale':
|
||||
|
|
@ -259,19 +273,19 @@
|
|||
|
||||
if (!match) return;
|
||||
|
||||
var fontStyle = match[1];
|
||||
// Font variant is not used
|
||||
// var fontVariant = match[2];
|
||||
var fontWeight = match[3];
|
||||
var fontSize = match[4];
|
||||
var lineHeight = match[5];
|
||||
var fontFamily = match[6];
|
||||
var fontStyle = match[1],
|
||||
// font variant is not used
|
||||
// fontVariant = match[2],
|
||||
fontWeight = match[3],
|
||||
fontSize = match[4],
|
||||
lineHeight = match[5],
|
||||
fontFamily = match[6];
|
||||
|
||||
if (fontStyle) {
|
||||
oStyle.fontStyle = fontStyle;
|
||||
}
|
||||
if (fontWeight) {
|
||||
oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
}
|
||||
if (fontSize) {
|
||||
oStyle.fontSize = parseFloat(fontSize);
|
||||
|
|
@ -359,22 +373,22 @@
|
|||
*/
|
||||
fabric.parseSVGDocument = (function() {
|
||||
|
||||
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/;
|
||||
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// \d doesn't quite cut it (as we need to match an actual float number)
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// \d doesn't quite cut it (as we need to match an actual float number)
|
||||
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
var reNum = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)';
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
|
||||
var reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*' +
|
||||
'$'
|
||||
);
|
||||
reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*' +
|
||||
'$'
|
||||
);
|
||||
|
||||
function hasAncestorWithNodeName(element, nodeName) {
|
||||
while (element && (element = element.parentNode)) {
|
||||
|
|
@ -391,10 +405,10 @@
|
|||
var startTime = new Date(),
|
||||
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
|
||||
|
||||
if (descendants.length === 0) {
|
||||
if (descendants.length === 0 && fabric.isLikelyNode) {
|
||||
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
|
||||
// https://github.com/ajaxorg/node-o3-xml/issues/21
|
||||
descendants = doc.selectNodes("//*[name(.)!='svg']");
|
||||
descendants = doc.selectNodes('//*[name(.)!="svg"]');
|
||||
var arr = [ ];
|
||||
for (var i = 0, len = descendants.length; i < len; i++) {
|
||||
arr[i] = descendants[i];
|
||||
|
|
@ -407,30 +421,43 @@
|
|||
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
||||
});
|
||||
|
||||
if (!elements || (elements && !elements.length)) return;
|
||||
if (!elements || (elements && !elements.length)) {
|
||||
callback && callback([], {});
|
||||
return;
|
||||
}
|
||||
|
||||
var viewBoxAttr = doc.getAttribute('viewBox'),
|
||||
widthAttr = doc.getAttribute('width'),
|
||||
heightAttr = doc.getAttribute('height'),
|
||||
widthAttr = parseFloat(doc.getAttribute('width')),
|
||||
heightAttr = parseFloat(doc.getAttribute('height')),
|
||||
width = null,
|
||||
height = null,
|
||||
viewBoxWidth,
|
||||
viewBoxHeight,
|
||||
minX,
|
||||
minY;
|
||||
|
||||
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
||||
minX = parseInt(viewBoxAttr[1], 10);
|
||||
minY = parseInt(viewBoxAttr[2], 10);
|
||||
width = parseInt(viewBoxAttr[3], 10);
|
||||
height = parseInt(viewBoxAttr[4], 10);
|
||||
minX = parseFloat(viewBoxAttr[1]);
|
||||
minY = parseFloat(viewBoxAttr[2]);
|
||||
viewBoxWidth = parseFloat(viewBoxAttr[3]);
|
||||
viewBoxHeight = parseFloat(viewBoxAttr[4]);
|
||||
}
|
||||
|
||||
// values of width/height attributes overwrite those extracted from viewbox attribute
|
||||
width = widthAttr ? parseFloat(widthAttr) : width;
|
||||
height = heightAttr ? parseFloat(heightAttr) : height;
|
||||
if (viewBoxWidth && widthAttr && viewBoxWidth !== widthAttr) {
|
||||
width = viewBoxWidth;
|
||||
height = viewBoxHeight;
|
||||
}
|
||||
else {
|
||||
// values of width/height attributes overwrite those extracted from viewbox attribute
|
||||
width = widthAttr ? widthAttr : viewBoxWidth;
|
||||
height = heightAttr ? heightAttr : viewBoxHeight;
|
||||
}
|
||||
|
||||
var options = {
|
||||
width: width,
|
||||
height: height
|
||||
height: height,
|
||||
widthAttr: widthAttr,
|
||||
heightAttr: heightAttr
|
||||
};
|
||||
|
||||
fabric.gradientDefs = fabric.getGradientDefs(doc);
|
||||
|
|
@ -614,7 +641,7 @@
|
|||
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
|
||||
*/
|
||||
parseElements: function(elements, callback, options, reviver) {
|
||||
fabric.ElementsParser.parse(elements, callback, options, reviver);
|
||||
new fabric.ElementsParser(elements, callback, options, reviver).parse();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -628,7 +655,9 @@
|
|||
var oStyle = { },
|
||||
style = element.getAttribute('style');
|
||||
|
||||
if (!style) return oStyle;
|
||||
if (!style) {
|
||||
return oStyle;
|
||||
}
|
||||
|
||||
if (typeof style === 'string') {
|
||||
parseStyleString(style, oStyle);
|
||||
|
|
@ -664,21 +693,27 @@
|
|||
len = points.length;
|
||||
for (; i < len; i++) {
|
||||
var pair = points[i].split(',');
|
||||
parsedPoints.push({ x: parseFloat(pair[0]), y: parseFloat(pair[1]) });
|
||||
parsedPoints.push({
|
||||
x: parseFloat(pair[0]),
|
||||
y: parseFloat(pair[1])
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
i = 0;
|
||||
len = points.length;
|
||||
for (; i < len; i+=2) {
|
||||
parsedPoints.push({ x: parseFloat(points[i]), y: parseFloat(points[i+1]) });
|
||||
parsedPoints.push({
|
||||
x: parseFloat(points[i]),
|
||||
y: parseFloat(points[i + 1])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// odd number of points is an error
|
||||
if (parsedPoints.length % 2 !== 0) {
|
||||
// if (parsedPoints.length % 2 !== 0) {
|
||||
// return null;
|
||||
}
|
||||
// }
|
||||
|
||||
return parsedPoints;
|
||||
},
|
||||
|
|
@ -758,13 +793,13 @@
|
|||
function onComplete(r) {
|
||||
|
||||
var xml = r.responseXML;
|
||||
if (!xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
|
||||
if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
|
||||
xml = new ActiveXObject('Microsoft.XMLDOM');
|
||||
xml.async = 'false';
|
||||
//IE chokes on DOCTYPE
|
||||
xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
|
||||
}
|
||||
if (!xml.documentElement) return;
|
||||
if (!xml || !xml.documentElement) return;
|
||||
|
||||
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
|
||||
svgCache.set(url, {
|
||||
|
|
|
|||
|
|
@ -101,10 +101,10 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* @return {String} SVG representation of a pattern
|
||||
*/
|
||||
toSVG: function(object) {
|
||||
var patternSource = typeof this.source === 'function' ? this.source() : this.source;
|
||||
var patternWidth = patternSource.width / object.getWidth();
|
||||
var patternHeight = patternSource.height / object.getHeight();
|
||||
var patternImgSrc = '';
|
||||
var patternSource = typeof this.source === 'function' ? this.source() : this.source,
|
||||
patternWidth = patternSource.width / object.getWidth(),
|
||||
patternHeight = patternSource.height / object.getHeight(),
|
||||
patternImgSrc = '';
|
||||
|
||||
if (patternSource.src) {
|
||||
patternImgSrc = patternSource.src;
|
||||
|
|
@ -133,11 +133,23 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* @return {CanvasPattern}
|
||||
*/
|
||||
toLive: function(ctx) {
|
||||
var source = typeof this.source === 'function' ? this.source() : this.source;
|
||||
var source = typeof this.source === 'function'
|
||||
? this.source()
|
||||
: this.source;
|
||||
|
||||
// if the image failed to load, return, and allow rest to continue loading
|
||||
if (!source) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// if an image
|
||||
if (typeof source.src !== 'undefined') {
|
||||
if (!source.complete) return '';
|
||||
if (source.naturalWidth === 0 || source.naturalHeight === 0) return '';
|
||||
if (!source.complete) {
|
||||
return '';
|
||||
}
|
||||
if (source.naturalWidth === 0 || source.naturalHeight === 0) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return ctx.createPattern(source, this.repeat);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
|
||||
|
||||
|
|
@ -250,7 +250,7 @@
|
|||
* @return {String}
|
||||
*/
|
||||
toString: function () {
|
||||
return this.x + "," + this.y;
|
||||
return this.x + ',' + this.y;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
@ -81,9 +81,8 @@
|
|||
* @return {Object} Shadow object with color, offsetX, offsetY and blur
|
||||
*/
|
||||
_parseShadow: function(shadow) {
|
||||
var shadowStr = shadow.trim();
|
||||
|
||||
var offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
|
||||
var shadowStr = shadow.trim(),
|
||||
offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
|
||||
color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
piBy2 = Math.PI * 2,
|
||||
|
|
@ -26,6 +26,13 @@
|
|||
*/
|
||||
type: 'circle',
|
||||
|
||||
/**
|
||||
* Radius of this circle
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
radius: 0,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} [options] Options object
|
||||
|
|
@ -99,7 +106,7 @@
|
|||
ctx.closePath();
|
||||
|
||||
this._renderFill(ctx);
|
||||
this._renderStroke(ctx);
|
||||
this.stroke && this._renderStroke(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global){
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
piBy2 = Math.PI * 2,
|
||||
|
|
@ -116,10 +116,10 @@
|
|||
}
|
||||
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
|
||||
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.rx, 0, piBy2, false);
|
||||
ctx.restore();
|
||||
|
||||
this._renderFill(ctx);
|
||||
this._renderStroke(ctx);
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -151,9 +151,9 @@
|
|||
fabric.Ellipse.fromElement = function(element, options) {
|
||||
options || (options = { });
|
||||
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
|
||||
var cx = parsedAttributes.left;
|
||||
var cy = parsedAttributes.top;
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES),
|
||||
cx = parsedAttributes.left,
|
||||
cy = parsedAttributes.top;
|
||||
|
||||
if ('left' in parsedAttributes) {
|
||||
parsedAttributes.left -= (options.width / 2) || 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global){
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
|
|
@ -232,7 +232,7 @@
|
|||
* @private
|
||||
*/
|
||||
_renderObject: function(object, ctx) {
|
||||
var v = this.canvas.viewportTransform,
|
||||
var v = this.getViewportTransform(),
|
||||
sxy = fabric.util.transformPoint(
|
||||
new fabric.Point(this.scaleX, this.scaleY),
|
||||
v,
|
||||
|
|
@ -409,11 +409,10 @@
|
|||
*/
|
||||
_setOpacityIfSame: function() {
|
||||
var objects = this.getObjects(),
|
||||
firstValue = objects[0] ? objects[0].get('opacity') : 1;
|
||||
|
||||
var isSameOpacity = objects.every(function(o) {
|
||||
return o.get('opacity') === firstValue;
|
||||
});
|
||||
firstValue = objects[0] ? objects[0].get('opacity') : 1,
|
||||
isSameOpacity = objects.every(function(o) {
|
||||
return o.get('opacity') === firstValue;
|
||||
});
|
||||
|
||||
if (isSameOpacity) {
|
||||
this.opacity = firstValue;
|
||||
|
|
@ -423,7 +422,7 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_calcBounds: function() {
|
||||
_calcBounds: function(onlyWidthHeight) {
|
||||
var aX = [],
|
||||
aY = [],
|
||||
o;
|
||||
|
|
@ -437,26 +436,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
this.set(this._getBounds(aX, aY));
|
||||
this.set(this._getBounds(aX, aY, onlyWidthHeight));
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getBounds: function(aX, aY) {
|
||||
_getBounds: function(aX, aY, onlyWidthHeight) {
|
||||
var ivt;
|
||||
if (this.canvas) {
|
||||
ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
|
||||
ivt = fabric.util.invertTransform(this.getViewportTransform());
|
||||
}
|
||||
var minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
|
||||
maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt);
|
||||
maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
|
||||
obj = {
|
||||
width: (maxXY.x - minXY.x) || 0,
|
||||
height: (maxXY.y - minXY.y) || 0
|
||||
};
|
||||
|
||||
return {
|
||||
width: (maxXY.x - minXY.x) || 0,
|
||||
height: (maxXY.y - minXY.y) || 0,
|
||||
left: (minXY.x + maxXY.x) / 2 || 0,
|
||||
top: (minXY.y + maxXY.y) / 2 || 0,
|
||||
};
|
||||
if (!onlyWidthHeight) {
|
||||
obj.left = (minXY.x + maxXY.x) / 2 || 0;
|
||||
obj.top = (minXY.y + maxXY.y) / 2 || 0;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
/* _TO_SVG_START_ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var extend = fabric.util.object.extend;
|
||||
|
||||
|
|
@ -122,17 +122,15 @@
|
|||
if (!this.visible) return;
|
||||
|
||||
ctx.save();
|
||||
var m = this.transformMatrix;
|
||||
var v;
|
||||
v = this.canvas.viewportTransform;
|
||||
|
||||
var isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
var m = this.transformMatrix,
|
||||
v = this.getViewportTransform(),
|
||||
isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
// this._resetWidthHeight();
|
||||
if (isInPathGroup) {
|
||||
ctx.translate(-this.group.width/2 + this.width/2, -this.group.height/2 + this.height/2);
|
||||
ctx.translate(-this.group.width/2, -this.group.height/2);
|
||||
}
|
||||
if (m) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
|
|
@ -140,6 +138,9 @@
|
|||
if (!noTransform) {
|
||||
this.transform(ctx);
|
||||
}
|
||||
if (isInPathGroup) {
|
||||
ctx.translate(this.width/2, this.height/2);
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
this._setShadow(ctx);
|
||||
|
|
@ -183,10 +184,10 @@
|
|||
this._setStrokeStyles(ctx);
|
||||
|
||||
ctx.beginPath();
|
||||
fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
|
||||
ctx.closePath();
|
||||
ctx.restore();
|
||||
},
|
||||
|
|
@ -253,7 +254,9 @@
|
|||
* @return {String} Source of an image
|
||||
*/
|
||||
getSrc: function() {
|
||||
return this.getElement().src || this.getElement()._src;
|
||||
if (this.getElement()) {
|
||||
return this.getElement().src || this.getElement()._src;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -282,6 +285,10 @@
|
|||
*/
|
||||
applyFilters: function(callback) {
|
||||
|
||||
if (!this._originalElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.filters.length === 0) {
|
||||
this._element = this._originalElement;
|
||||
callback && callback();
|
||||
|
|
@ -331,13 +338,13 @@
|
|||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
ctx.drawImage(
|
||||
this._element,
|
||||
-this.width / 2,
|
||||
-this.height / 2,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
this._element && ctx.drawImage(
|
||||
this._element,
|
||||
-this.width / 2,
|
||||
-this.height / 2,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -369,7 +376,9 @@
|
|||
options || (options = { });
|
||||
this.setOptions(options);
|
||||
this._setWidthHeight(options);
|
||||
this._element.crossOrigin = this.crossOrigin;
|
||||
if (this._element && this.crossOrigin) {
|
||||
this._element.crossOrigin = this.crossOrigin;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -395,11 +404,15 @@
|
|||
_setWidthHeight: function(options) {
|
||||
this.width = 'width' in options
|
||||
? options.width
|
||||
: (this.getElement().width || 0);
|
||||
: (this.getElement()
|
||||
? this.getElement().width || 0
|
||||
: 0);
|
||||
|
||||
this.height = 'height' in options
|
||||
? options.height
|
||||
: (this.getElement().height || 0);
|
||||
: (this.getElement()
|
||||
? this.getElement().height || 0
|
||||
: 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -417,7 +430,7 @@
|
|||
* @type String
|
||||
* @default
|
||||
*/
|
||||
fabric.Image.CSS_CANVAS = "canvas-img";
|
||||
fabric.Image.CSS_CANVAS = 'canvas-img';
|
||||
|
||||
/**
|
||||
* Alias for getSrc
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
* @extends fabric.Text
|
||||
* @mixes fabric.Observable
|
||||
*
|
||||
* @fires text:changed
|
||||
* @fires editing:entered
|
||||
* @fires editing:exited
|
||||
* @fires changed ("text:changed" when observing canvas)
|
||||
* @fires editing:entered ("text:editing:entered" when observing canvas)
|
||||
* @fires editing:exited ("text:editing:exited" when observing canvas)
|
||||
*
|
||||
* @return {fabric.IText} thisArg
|
||||
* @see {@link fabric.IText#initialize} for constructor definition
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
* Copy text: ctrl/cmd + c
|
||||
* Paste text: ctrl/cmd + v
|
||||
* Cut text: ctrl/cmd + x
|
||||
* Select entire text: ctrl/cmd + a
|
||||
* </pre>
|
||||
*
|
||||
* <p>Supported mouse/touch combination</p>
|
||||
|
|
@ -217,6 +218,9 @@
|
|||
* @param {Number} index Index to set selection start to
|
||||
*/
|
||||
setSelectionStart: function(index) {
|
||||
if (this.selectionStart !== index) {
|
||||
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
|
||||
}
|
||||
this.selectionStart = index;
|
||||
this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index);
|
||||
},
|
||||
|
|
@ -226,6 +230,9 @@
|
|||
* @param {Number} index Index to set selection end to
|
||||
*/
|
||||
setSelectionEnd: function(index) {
|
||||
if (this.selectionEnd !== index) {
|
||||
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
|
||||
}
|
||||
this.selectionEnd = index;
|
||||
this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index);
|
||||
},
|
||||
|
|
@ -289,9 +296,9 @@
|
|||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
this.callSuper('_render', ctx);
|
||||
this.ctx = ctx;
|
||||
|
|
@ -325,8 +332,8 @@
|
|||
if (typeof selectionStart === 'undefined') {
|
||||
selectionStart = this.selectionStart;
|
||||
}
|
||||
var textBeforeCursor = this.text.slice(0, selectionStart);
|
||||
var linesBeforeCursor = textBeforeCursor.split(this._reNewline);
|
||||
var textBeforeCursor = this.text.slice(0, selectionStart),
|
||||
linesBeforeCursor = textBeforeCursor.split(this._reNewline);
|
||||
|
||||
return {
|
||||
lineIndex: linesBeforeCursor.length - 1,
|
||||
|
|
@ -334,6 +341,26 @@
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns complete style of char at the current cursor
|
||||
* @param {Number} lineIndex Line index
|
||||
* @param {Number} charIndex Char index
|
||||
* @return {Object} Character style
|
||||
*/
|
||||
getCurrentCharStyle: function(lineIndex, charIndex) {
|
||||
var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
|
||||
|
||||
return {
|
||||
fontSize: style && style.fontSize || this.fontSize,
|
||||
fill: style && style.fill || this.fill,
|
||||
textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
|
||||
textDecoration: style && style.textDecoration || this.textDecoration,
|
||||
fontFamily: style && style.fontFamily || this.fontFamily,
|
||||
stroke: style && style.stroke || this.stroke,
|
||||
strokeWidth: style && style.strokeWidth || this.strokeWidth
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns fontSize of char at the current cursor
|
||||
* @param {Number} lineIndex Line index
|
||||
|
|
@ -480,13 +507,16 @@
|
|||
var cursorLocation = this.get2DCursorLocation(),
|
||||
lineIndex = cursorLocation.lineIndex,
|
||||
charIndex = cursorLocation.charIndex,
|
||||
charHeight = this.getCurrentCharFontSize(lineIndex, charIndex);
|
||||
charHeight = this.getCurrentCharFontSize(lineIndex, charIndex),
|
||||
leftOffset = (lineIndex === 0 && charIndex === 0)
|
||||
? this._getCachedLineOffset(lineIndex, this.text.split(this._reNewline))
|
||||
: boundaries.leftOffset;
|
||||
|
||||
ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex);
|
||||
ctx.globalAlpha = this._currentCursorOpacity;
|
||||
ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
|
||||
|
||||
ctx.fillRect(
|
||||
boundaries.left + boundaries.leftOffset,
|
||||
boundaries.left + leftOffset,
|
||||
boundaries.top + boundaries.topOffset,
|
||||
this.cursorWidth / this.scaleX,
|
||||
charHeight);
|
||||
|
|
@ -506,39 +536,43 @@
|
|||
|
||||
ctx.fillStyle = this.selectionColor;
|
||||
|
||||
var cursorLocation = this.get2DCursorLocation(),
|
||||
lineIndex = cursorLocation.lineIndex,
|
||||
charIndex = cursorLocation.charIndex,
|
||||
textLines = this.text.split(this._reNewline),
|
||||
origLineIndex = lineIndex;
|
||||
var start = this.get2DCursorLocation(this.selectionStart),
|
||||
end = this.get2DCursorLocation(this.selectionEnd),
|
||||
startLine = start.lineIndex,
|
||||
endLine = end.lineIndex,
|
||||
textLines = this.text.split(this._reNewline);
|
||||
|
||||
for (var i = this.selectionStart; i < this.selectionEnd; i++) {
|
||||
for (var i = startLine; i <= endLine; i++) {
|
||||
var lineOffset = this._getCachedLineOffset(i, textLines) || 0,
|
||||
lineHeight = this._getCachedLineHeight(i),
|
||||
boxWidth = 0;
|
||||
|
||||
if (chars[i] === '\n') {
|
||||
boundaries.leftOffset = 0;
|
||||
boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex);
|
||||
lineIndex++;
|
||||
charIndex = 0;
|
||||
}
|
||||
else if (i !== this.text.length) {
|
||||
|
||||
var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex),
|
||||
lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0;
|
||||
|
||||
if (lineIndex === origLineIndex) {
|
||||
// only offset the line if we're rendering selection of 2nd, 3rd, etc. line
|
||||
lineOffset = 0;
|
||||
if (i === startLine) {
|
||||
for (var j = 0, len = textLines[i].length; j < len; j++) {
|
||||
if (j >= start.charIndex && (i !== endLine || j < end.charIndex)) {
|
||||
boxWidth += this._getWidthOfChar(ctx, textLines[i][j], i, j);
|
||||
}
|
||||
if (j < start.charIndex) {
|
||||
lineOffset += this._getWidthOfChar(ctx, textLines[i][j], i, j);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillRect(
|
||||
boundaries.left + boundaries.leftOffset + lineOffset,
|
||||
boundaries.top + boundaries.topOffset,
|
||||
charWidth,
|
||||
this._getHeightOfLine(ctx, lineIndex));
|
||||
|
||||
boundaries.leftOffset += charWidth;
|
||||
charIndex++;
|
||||
}
|
||||
else if (i > startLine && i < endLine) {
|
||||
boxWidth += this._getCachedLineWidth(i, textLines) || 5;
|
||||
}
|
||||
else if (i === endLine) {
|
||||
for (var j2 = 0, j2len = end.charIndex; j2 < j2len; j2++) {
|
||||
boxWidth += this._getWidthOfChar(ctx, textLines[i][j2], i, j2);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillRect(
|
||||
boundaries.left + lineOffset,
|
||||
boundaries.top + boundaries.topOffset,
|
||||
boxWidth,
|
||||
lineHeight);
|
||||
|
||||
boundaries.topOffset += lineHeight;
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
|
@ -568,14 +602,26 @@
|
|||
lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines),
|
||||
lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines),
|
||||
lineLeftOffset = this._getLineLeftOffset(lineWidth),
|
||||
chars = line.split('');
|
||||
chars = line.split(''),
|
||||
prevStyle,
|
||||
charsToRender = '';
|
||||
|
||||
left += lineLeftOffset || 0;
|
||||
|
||||
ctx.save();
|
||||
for (var i = 0, len = chars.length; i < len; i++) {
|
||||
this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight);
|
||||
|
||||
for (var i = 0, len = chars.length; i <= len; i++) {
|
||||
prevStyle = prevStyle || this.getCurrentCharStyle(lineIndex, i);
|
||||
var thisStyle = this.getCurrentCharStyle(lineIndex, i + 1);
|
||||
|
||||
if (this._hasStyleChanged(prevStyle, thisStyle) || i === len) {
|
||||
this._renderChar(method, ctx, lineIndex, i - 1, charsToRender, left, top, lineHeight);
|
||||
charsToRender = '';
|
||||
prevStyle = thisStyle;
|
||||
}
|
||||
charsToRender += chars[i];
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
|
|
@ -638,6 +684,22 @@
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} prevStyle
|
||||
* @param {Object} thisStyle
|
||||
*/
|
||||
_hasStyleChanged: function(prevStyle, thisStyle) {
|
||||
return (prevStyle.fill !== thisStyle.fill ||
|
||||
prevStyle.fontSize !== thisStyle.fontSize ||
|
||||
prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
|
||||
prevStyle.textDecoration !== thisStyle.textDecoration ||
|
||||
prevStyle.fontFamily !== thisStyle.fontFamily ||
|
||||
prevStyle.stroke !== thisStyle.stroke ||
|
||||
prevStyle.strokeWidth !== thisStyle.strokeWidth
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
@ -645,10 +707,10 @@
|
|||
_renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight, charHeight) {
|
||||
|
||||
var textDecoration = styleDeclaration
|
||||
? (styleDeclaration.textDecoration || this.textDecoration)
|
||||
: this.textDecoration;
|
||||
? (styleDeclaration.textDecoration || this.textDecoration)
|
||||
: this.textDecoration,
|
||||
|
||||
var fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
|
||||
fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
|
||||
|
||||
if (!textDecoration) return;
|
||||
|
||||
|
|
@ -868,15 +930,35 @@
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
*/
|
||||
_getStyleDeclaration: function(lineIndex, charIndex) {
|
||||
return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
|
||||
? clone(this.styles[lineIndex][charIndex])
|
||||
: { };
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
|
||||
ctx.save();
|
||||
var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
|
||||
ctx.restore();
|
||||
return width;
|
||||
var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
|
||||
this._applyFontStyles(styleDeclaration);
|
||||
var cacheProp = this._getCacheProp(_char, styleDeclaration);
|
||||
|
||||
if (this._charWidthsCache[cacheProp] && this.caching) {
|
||||
return this._charWidthsCache[cacheProp];
|
||||
}
|
||||
else if (ctx) {
|
||||
ctx.save();
|
||||
var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
|
||||
ctx.restore();
|
||||
return width;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -962,10 +1044,9 @@
|
|||
|
||||
textLines = textLines || this.text.split(this._reNewline);
|
||||
|
||||
var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0);
|
||||
|
||||
var line = textLines[lineIndex];
|
||||
var chars = line.split('');
|
||||
var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0),
|
||||
line = textLines[lineIndex],
|
||||
chars = line.split('');
|
||||
|
||||
for (var i = 1, len = chars.length; i < len; i++) {
|
||||
var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i);
|
||||
|
|
@ -998,6 +1079,26 @@
|
|||
return topOffset - (this.fontSize / this._fontSizeFraction);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* This method is overwritten to account for different top offset
|
||||
*/
|
||||
_renderTextBoxBackground: function(ctx) {
|
||||
if (!this.backgroundColor) return;
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = this.backgroundColor;
|
||||
|
||||
ctx.fillRect(
|
||||
this._getLeftOffset(),
|
||||
this._getTopOffset() + (this.fontSize / this._fontSizeFraction),
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns object representation of an instance
|
||||
* @methd toObject
|
||||
|
|
@ -1022,6 +1123,12 @@
|
|||
return new fabric.IText(object.text, clone(object));
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains all fabric.IText objects that have been created
|
||||
* @static
|
||||
* @memberof fabric.IText
|
||||
* @type Array
|
||||
*/
|
||||
fabric.IText.instances = [ ];
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
coordProps = { 'x1': 1, 'x2': 1, 'y1': 1, 'y2': 1 },
|
||||
coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
|
||||
supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
|
||||
|
||||
if (fabric.Line) {
|
||||
|
|
@ -27,6 +27,34 @@
|
|||
*/
|
||||
type: 'line',
|
||||
|
||||
/**
|
||||
* x value or first line edge
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
x1: 0,
|
||||
|
||||
/**
|
||||
* y value or first line edge
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
y1: 0,
|
||||
|
||||
/**
|
||||
* x value or second line edge
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
x2: 0,
|
||||
|
||||
/**
|
||||
* y value or second line edge
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
y2: 0,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Array} [points] Array of points
|
||||
|
|
@ -57,11 +85,16 @@
|
|||
_setWidthHeight: function(options) {
|
||||
options || (options = { });
|
||||
|
||||
this.set('width', Math.abs(this.x2 - this.x1) || 1);
|
||||
this.set('height', Math.abs(this.y2 - this.y1) || 1);
|
||||
this.width = Math.abs(this.x2 - this.x1) || 1;
|
||||
this.height = Math.abs(this.y2 - this.y1) || 1;
|
||||
|
||||
this.set('left', 'left' in options ? options.left : (Math.min(this.x1, this.x2) + this.width / 2));
|
||||
this.set('top', 'top' in options ? options.top : (Math.min(this.y1, this.y2) + this.height / 2));
|
||||
this.left = 'left' in options
|
||||
? options.left
|
||||
: this._getLeftToOriginX();
|
||||
|
||||
this.top = 'top' in options
|
||||
? options.top
|
||||
: this._getTopToOriginY();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -71,12 +104,48 @@
|
|||
*/
|
||||
_set: function(key, value) {
|
||||
this[key] = value;
|
||||
if (key in coordProps) {
|
||||
if (typeof coordProps[key] !== 'undefined') {
|
||||
this._setWidthHeight();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
|
||||
*/
|
||||
_getLeftToOriginX: makeEdgeToOriginGetter(
|
||||
{ // property names
|
||||
origin: 'originX',
|
||||
axis1: 'x1',
|
||||
axis2: 'x2',
|
||||
dimension: 'width'
|
||||
},
|
||||
{ // possible values of origin
|
||||
nearest: 'left',
|
||||
center: 'center',
|
||||
farthest: 'right'
|
||||
}
|
||||
),
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
|
||||
*/
|
||||
_getTopToOriginY: makeEdgeToOriginGetter(
|
||||
{ // property names
|
||||
origin: 'originY',
|
||||
axis1: 'y1',
|
||||
axis2: 'y2',
|
||||
dimension: 'height'
|
||||
},
|
||||
{ // possible values of origin
|
||||
nearest: 'top',
|
||||
center: 'center',
|
||||
farthest: 'bottom'
|
||||
}
|
||||
),
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
@ -86,15 +155,23 @@
|
|||
|
||||
var isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
if (isInPathGroup && !this.transformMatrix) {
|
||||
ctx.translate(-this.group.width/2 + this.left, -this.group.height / 2 + this.top);
|
||||
// Line coords are distances from left-top of canvas to origin of line.
|
||||
//
|
||||
// To render line in a path-group, we need to translate them to
|
||||
// distances from center of path-group to center of line.
|
||||
var cp = this.getCenterPoint();
|
||||
ctx.translate(
|
||||
-this.group.width/2 + cp.x,
|
||||
-this.group.height / 2 + cp.y
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
|
||||
|
||||
// move from center (of virtual box) to its left/top corner
|
||||
// we can't assume x1, y1 is top left and x2, y2 is bottom right
|
||||
var xMult = this.x1 <= this.x2 ? -1 : 1;
|
||||
var yMult = this.y1 <= this.y2 ? -1 : 1;
|
||||
var xMult = this.x1 <= this.x2 ? -1 : 1,
|
||||
yMult = this.y1 <= this.y2 ? -1 : 1;
|
||||
|
||||
ctx.moveTo(
|
||||
this.width === 1 ? 0 : (xMult * this.width / 2),
|
||||
|
|
@ -112,7 +189,7 @@
|
|||
// (by copying fillStyle to strokeStyle, since line is stroked, not filled)
|
||||
var origStrokeStyle = ctx.strokeStyle;
|
||||
ctx.strokeStyle = this.stroke || ctx.fillStyle;
|
||||
this._renderStroke(ctx);
|
||||
this.stroke && this._renderStroke(ctx);
|
||||
ctx.strokeStyle = origStrokeStyle;
|
||||
},
|
||||
|
||||
|
|
@ -197,13 +274,13 @@
|
|||
* @return {fabric.Line} instance of fabric.Line
|
||||
*/
|
||||
fabric.Line.fromElement = function(element, options) {
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES);
|
||||
var points = [
|
||||
parsedAttributes.x1 || 0,
|
||||
parsedAttributes.y1 || 0,
|
||||
parsedAttributes.x2 || 0,
|
||||
parsedAttributes.y2 || 0
|
||||
];
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
|
||||
points = [
|
||||
parsedAttributes.x1 || 0,
|
||||
parsedAttributes.y1 || 0,
|
||||
parsedAttributes.x2 || 0,
|
||||
parsedAttributes.y2 || 0
|
||||
];
|
||||
return new fabric.Line(points, extend(parsedAttributes, options));
|
||||
};
|
||||
/* _FROM_SVG_END_ */
|
||||
|
|
@ -220,4 +297,29 @@
|
|||
return new fabric.Line(points, object);
|
||||
};
|
||||
|
||||
/**
|
||||
* Produces a function that calculates distance from canvas edge to Line origin.
|
||||
*/
|
||||
function makeEdgeToOriginGetter(propertyNames, originValues) {
|
||||
var origin = propertyNames.origin,
|
||||
axis1 = propertyNames.axis1,
|
||||
axis2 = propertyNames.axis2,
|
||||
dimension = propertyNames.dimension,
|
||||
nearest = originValues.nearest,
|
||||
center = originValues.center,
|
||||
farthest = originValues.farthest;
|
||||
|
||||
return function() {
|
||||
switch (this.get(origin)) {
|
||||
case nearest:
|
||||
return Math.min(this.get(axis1), this.get(axis2));
|
||||
case center:
|
||||
return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
|
||||
case farthest:
|
||||
return Math.max(this.get(axis1), this.get(axis2));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
|
|
@ -611,6 +611,7 @@
|
|||
|
||||
/**
|
||||
* Function that determines clipping of an object (context is passed as a first argument)
|
||||
* Note that context origin is at the object's center point (not left/top corner)
|
||||
* @type Function
|
||||
*/
|
||||
clipTo: null,
|
||||
|
|
@ -752,34 +753,34 @@
|
|||
*/
|
||||
toObject: function(propertiesToInclude) {
|
||||
|
||||
var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
|
||||
var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
|
||||
|
||||
var object = {
|
||||
type: this.type,
|
||||
originX: this.originX,
|
||||
originY: this.originY,
|
||||
left: toFixed(this.left, NUM_FRACTION_DIGITS),
|
||||
top: toFixed(this.top, NUM_FRACTION_DIGITS),
|
||||
width: toFixed(this.width, NUM_FRACTION_DIGITS),
|
||||
height: toFixed(this.height, NUM_FRACTION_DIGITS),
|
||||
fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
|
||||
stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
|
||||
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
|
||||
strokeDashArray: this.strokeDashArray,
|
||||
strokeLineCap: this.strokeLineCap,
|
||||
strokeLineJoin: this.strokeLineJoin,
|
||||
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
|
||||
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
|
||||
scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
|
||||
angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
|
||||
flipX: this.flipX,
|
||||
flipY: this.flipY,
|
||||
opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
|
||||
shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
|
||||
visible: this.visible,
|
||||
clipTo: this.clipTo && String(this.clipTo),
|
||||
backgroundColor: this.backgroundColor
|
||||
};
|
||||
object = {
|
||||
type: this.type,
|
||||
originX: this.originX,
|
||||
originY: this.originY,
|
||||
left: toFixed(this.left, NUM_FRACTION_DIGITS),
|
||||
top: toFixed(this.top, NUM_FRACTION_DIGITS),
|
||||
width: toFixed(this.width, NUM_FRACTION_DIGITS),
|
||||
height: toFixed(this.height, NUM_FRACTION_DIGITS),
|
||||
fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
|
||||
stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
|
||||
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
|
||||
strokeDashArray: this.strokeDashArray,
|
||||
strokeLineCap: this.strokeLineCap,
|
||||
strokeLineJoin: this.strokeLineJoin,
|
||||
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
|
||||
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
|
||||
scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
|
||||
angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
|
||||
flipX: this.flipX,
|
||||
flipY: this.flipY,
|
||||
opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
|
||||
shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
|
||||
visible: this.visible,
|
||||
clipTo: this.clipTo && String(this.clipTo),
|
||||
backgroundColor: this.backgroundColor
|
||||
};
|
||||
|
||||
if (!this.includeDefaultValues) {
|
||||
object = this._removeDefaultValues(object);
|
||||
|
|
@ -805,8 +806,8 @@
|
|||
* @param {Object} object
|
||||
*/
|
||||
_removeDefaultValues: function(object) {
|
||||
var prototype = fabric.util.getKlass(object.type).prototype;
|
||||
var stateProperties = prototype.stateProperties;
|
||||
var prototype = fabric.util.getKlass(object.type).prototype,
|
||||
stateProperties = prototype.stateProperties;
|
||||
|
||||
stateProperties.forEach(function(prop) {
|
||||
if (object[prop] === prototype[prop]) {
|
||||
|
|
@ -822,7 +823,7 @@
|
|||
* @return {String}
|
||||
*/
|
||||
toString: function() {
|
||||
return "#<fabric." + capitalize(this.type) + ">";
|
||||
return '#<fabric.' + capitalize(this.type) + '>';
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -834,6 +835,15 @@
|
|||
return this[property];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_setObject: function(obj) {
|
||||
for (var prop in obj) {
|
||||
this._set(prop, obj[prop]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
|
||||
* @param {String|Object} key Property name or object (if object, iterate over the object properties)
|
||||
|
|
@ -843,9 +853,7 @@
|
|||
*/
|
||||
set: function(key, value) {
|
||||
if (typeof key === 'object') {
|
||||
for (var prop in key) {
|
||||
this._set(prop, key[prop]);
|
||||
}
|
||||
this._setObject(key);
|
||||
}
|
||||
else {
|
||||
if (typeof value === 'function' && key !== 'clipTo') {
|
||||
|
|
@ -915,6 +923,18 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves viewportTransform from Object's canvas if possible
|
||||
* @method getViewportTransform
|
||||
* @memberOf fabric.Object.prototype
|
||||
* @return {Boolean} flipY value // TODO
|
||||
*/
|
||||
getViewportTransform: function() {
|
||||
if (this.canvas && this.canvas.viewportTransform)
|
||||
return this.canvas.viewportTransform;
|
||||
return [1, 0, 0, 1, 0, 0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders an object on a specified context
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
@ -926,6 +946,9 @@
|
|||
|
||||
ctx.save();
|
||||
|
||||
//setup fill rule for current object
|
||||
this._setupFillRule(ctx);
|
||||
|
||||
this._transform(ctx, noTransform);
|
||||
this._setStrokeStyles(ctx);
|
||||
this._setFillStyles(ctx);
|
||||
|
|
@ -941,6 +964,8 @@
|
|||
this._render(ctx, noTransform);
|
||||
this.clipTo && ctx.restore();
|
||||
this._removeShadow(ctx);
|
||||
this._restoreFillRule(ctx);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
this._renderControls(ctx, noTransform);
|
||||
|
|
@ -948,7 +973,7 @@
|
|||
|
||||
_transform: function(ctx, noTransform) {
|
||||
var m = this.transformMatrix;
|
||||
var v = this.canvas.viewportTransform;
|
||||
var v = this.getViewportTransform();
|
||||
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
|
|
@ -986,7 +1011,7 @@
|
|||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
_renderControls: function(ctx, noTransform) {
|
||||
var v = this.canvas.viewportTransform;
|
||||
var v = this.getViewportTransform();
|
||||
|
||||
ctx.save();
|
||||
if (this.active && !noTransform) {
|
||||
|
|
@ -1027,6 +1052,8 @@
|
|||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_removeShadow: function(ctx) {
|
||||
if (!this.shadow) return;
|
||||
|
||||
ctx.shadowColor = '';
|
||||
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
|
||||
},
|
||||
|
|
@ -1044,7 +1071,12 @@
|
|||
-this.width / 2 + this.fill.offsetX || 0,
|
||||
-this.height / 2 + this.fill.offsetY || 0);
|
||||
}
|
||||
ctx.fill();
|
||||
if (this.fillRule === 'destination-over') {
|
||||
ctx.fill('evenodd');
|
||||
}
|
||||
else {
|
||||
ctx.fill();
|
||||
}
|
||||
if (this.fill.toLive) {
|
||||
ctx.restore();
|
||||
}
|
||||
|
|
@ -1243,7 +1275,7 @@
|
|||
setGradient: function(property, options) {
|
||||
options || (options = { });
|
||||
|
||||
var gradient = {colorStops: []};
|
||||
var gradient = { colorStops: [] };
|
||||
|
||||
gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
|
||||
gradient.coords = {
|
||||
|
|
@ -1260,7 +1292,11 @@
|
|||
|
||||
for (var position in options.colorStops) {
|
||||
var color = new fabric.Color(options.colorStops[position]);
|
||||
gradient.colorStops.push({offset: position, color: color.toRgb(), opacity: color.getAlpha()});
|
||||
gradient.colorStops.push({
|
||||
offset: position,
|
||||
color: color.toRgb(),
|
||||
opacity: color.getAlpha()
|
||||
});
|
||||
}
|
||||
|
||||
return this.set(property, fabric.Gradient.forObject(this, gradient));
|
||||
|
|
@ -1318,7 +1354,7 @@
|
|||
/**
|
||||
* Sets "color" of an instance (alias of `set('fill', …)`)
|
||||
* @param {String} color Color value
|
||||
* @return {fabric.Text} thisArg
|
||||
* @return {fabric.Object} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setColor: function(color) {
|
||||
|
|
@ -1326,6 +1362,28 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets "angle" of an instance
|
||||
* @param {Number} angle Angle value
|
||||
* @return {fabric.Object} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setAngle: function(angle) {
|
||||
var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
|
||||
|
||||
if (shouldCenterOrigin) {
|
||||
this._setOriginToCenter();
|
||||
}
|
||||
|
||||
this.set('angle', angle);
|
||||
|
||||
if (shouldCenterOrigin) {
|
||||
this._resetOrigin();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Centers object horizontally on canvas to which it was added last.
|
||||
* You might need to call `setCoords` on an object after centering, to update controls area.
|
||||
|
|
@ -1365,7 +1423,8 @@
|
|||
* @chainable
|
||||
*/
|
||||
remove: function() {
|
||||
return this.canvas.remove(this);
|
||||
this.canvas.remove(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -1381,6 +1440,28 @@
|
|||
x: pointer.x - objectLeftTop.x,
|
||||
y: pointer.y - objectLeftTop.y
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets canvas globalCompositeOperation for specific object
|
||||
* custom composition operation for the particular object can be specifed using fillRule property
|
||||
* @param {CanvasRenderingContext2D} ctx Rendering canvas context
|
||||
*/
|
||||
_setupFillRule: function (ctx) {
|
||||
if (this.fillRule) {
|
||||
this._prevFillRule = ctx.globalCompositeOperation;
|
||||
ctx.globalCompositeOperation = this.fillRule;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Restores previously saved canvas globalCompositeOperation after obeject rendering
|
||||
* @param {CanvasRenderingContext2D} ctx Rendering canvas context
|
||||
*/
|
||||
_restoreFillRule: function (ctx) {
|
||||
if (this.fillRule && this._prevFillRule) {
|
||||
ctx.globalCompositeOperation = this._prevFillRule;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
(function(global) {
|
||||
|
||||
var commandLengths = {
|
||||
m: 2,
|
||||
l: 2,
|
||||
h: 1,
|
||||
v: 1,
|
||||
c: 6,
|
||||
s: 4,
|
||||
q: 4,
|
||||
t: 2,
|
||||
a: 7
|
||||
};
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
min = fabric.util.array.min,
|
||||
max = fabric.util.array.max,
|
||||
extend = fabric.util.object.extend,
|
||||
_toString = Object.prototype.toString,
|
||||
drawArc = fabric.util.drawArc;
|
||||
drawArc = fabric.util.drawArc,
|
||||
commandLengths = {
|
||||
m: 2,
|
||||
l: 2,
|
||||
h: 1,
|
||||
v: 1,
|
||||
c: 6,
|
||||
s: 4,
|
||||
q: 4,
|
||||
t: 2,
|
||||
a: 7
|
||||
};
|
||||
|
||||
if (fabric.Path) {
|
||||
fabric.warn('fabric.Path is already defined');
|
||||
|
|
@ -62,6 +61,13 @@
|
|||
*/
|
||||
type: 'path',
|
||||
|
||||
/**
|
||||
* Array of path points
|
||||
* @type Array
|
||||
* @default
|
||||
*/
|
||||
path: null,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
|
||||
|
|
@ -125,7 +131,9 @@
|
|||
this.left = this.width / 2;
|
||||
}
|
||||
}
|
||||
this.pathOffset = this.pathOffset || this._calculatePathOffset(origLeft, origTop); //Save top-left coords as offset
|
||||
this.pathOffset = this.pathOffset ||
|
||||
// Save top-left coords as offset
|
||||
this._calculatePathOffset(origLeft, origTop);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -281,8 +289,8 @@
|
|||
tempX = current[3];
|
||||
tempY = current[4];
|
||||
// calculate reflection of previous control points
|
||||
controlX = 2*x - controlX;
|
||||
controlY = 2*y - controlY;
|
||||
controlX = 2 * x - controlX;
|
||||
controlY = 2 * y - controlY;
|
||||
ctx.bezierCurveTo(
|
||||
controlX + l,
|
||||
controlY + t,
|
||||
|
|
@ -343,7 +351,6 @@
|
|||
tempX = x + current[1];
|
||||
tempY = y + current[2];
|
||||
|
||||
|
||||
if (previous[0].match(/[QqTt]/) === null) {
|
||||
// If there is no previous command or if the previous command was not a Q, q, T or t,
|
||||
// assume the control point is coincident with the current point
|
||||
|
|
@ -444,7 +451,7 @@
|
|||
ctx.save();
|
||||
var m = this.transformMatrix;
|
||||
|
||||
var v = this.canvas.viewportTransform;
|
||||
var v = this.getViewportTransform();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
if (m) {
|
||||
|
|
@ -485,7 +492,7 @@
|
|||
*/
|
||||
toObject: function(propertiesToInclude) {
|
||||
var o = extend(this.callSuper('toObject', propertiesToInclude), {
|
||||
path: this.path,
|
||||
path: this.path.map(function(item) { return item.slice() }),
|
||||
pathOffset: this.pathOffset
|
||||
});
|
||||
if (this.sourcePath) {
|
||||
|
|
@ -557,7 +564,7 @@
|
|||
coords = [ ],
|
||||
currentPath,
|
||||
parsed,
|
||||
re = /(-?\.\d+)|(-?\d+(\.\d+)?)/g,
|
||||
re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
|
||||
match,
|
||||
coordsStr;
|
||||
|
||||
|
|
@ -613,14 +620,14 @@
|
|||
maxX = max(aX),
|
||||
maxY = max(aY),
|
||||
deltaX = maxX - minX,
|
||||
deltaY = maxY - minY;
|
||||
deltaY = maxY - minY,
|
||||
|
||||
var o = {
|
||||
left: this.left + (minX + deltaX / 2),
|
||||
top: this.top + (minY + deltaY / 2),
|
||||
width: deltaX,
|
||||
height: deltaY
|
||||
};
|
||||
o = {
|
||||
left: this.left + (minX + deltaX / 2),
|
||||
top: this.top + (minY + deltaY / 2),
|
||||
width: deltaX,
|
||||
height: deltaY
|
||||
};
|
||||
|
||||
return o;
|
||||
},
|
||||
|
|
@ -641,13 +648,18 @@
|
|||
isLowerCase = true;
|
||||
}
|
||||
|
||||
var xy = this._getXY(item, isLowerCase, previous);
|
||||
var xy = this._getXY(item, isLowerCase, previous),
|
||||
val;
|
||||
|
||||
var val = parseInt(xy.x, 10);
|
||||
if (!isNaN(val)) aX.push(val);
|
||||
val = parseInt(xy.x, 10);
|
||||
if (!isNaN(val)) {
|
||||
aX.push(val);
|
||||
}
|
||||
|
||||
val = parseInt(xy.y, 10);
|
||||
if (!isNaN(val)) aY.push(val);
|
||||
if (!isNaN(val)) {
|
||||
aY.push(val);
|
||||
}
|
||||
},
|
||||
|
||||
_getXY: function(item, isLowerCase, previous) {
|
||||
|
|
@ -659,13 +671,13 @@
|
|||
? previous.x + getX(item)
|
||||
: item[0] === 'V'
|
||||
? previous.x
|
||||
: getX(item);
|
||||
: getX(item),
|
||||
|
||||
var y = isLowerCase
|
||||
? previous.y + getY(item)
|
||||
: item[0] === 'H'
|
||||
? previous.y
|
||||
: getY(item);
|
||||
y = isLowerCase
|
||||
? previous.y + getY(item)
|
||||
: item[0] === 'H'
|
||||
? previous.y
|
||||
: getY(item);
|
||||
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
|
@ -681,9 +693,9 @@
|
|||
fabric.Path.fromObject = function(object, callback) {
|
||||
if (typeof object.path === 'string') {
|
||||
fabric.loadSVGFromURL(object.path, function (elements) {
|
||||
var path = elements[0];
|
||||
var path = elements[0],
|
||||
pathUrl = object.path;
|
||||
|
||||
var pathUrl = object.path;
|
||||
delete object.path;
|
||||
|
||||
fabric.util.object.extend(path, object);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
|
|
@ -52,6 +52,15 @@
|
|||
|
||||
this.setOptions(options);
|
||||
|
||||
if (options.widthAttr) {
|
||||
this.scaleX = options.widthAttr / options.width;
|
||||
}
|
||||
if (options.heightAttr) {
|
||||
this.scaleY = options.heightAttr / options.height;
|
||||
}
|
||||
|
||||
this.setCoords();
|
||||
|
||||
if (options.sourcePath) {
|
||||
this.setSourcePath(options.sourcePath);
|
||||
}
|
||||
|
|
@ -69,7 +78,7 @@
|
|||
|
||||
var m = this.transformMatrix;
|
||||
|
||||
var v = this.canvas.viewportTransform;
|
||||
var v = this.getViewportTransform();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
if (m) {
|
||||
|
|
@ -143,13 +152,13 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var objects = this.getObjects();
|
||||
var markup = [
|
||||
'<g ',
|
||||
'style="', this.getSvgStyles(), '" ',
|
||||
'transform="', this.getSvgTransform(), '" ',
|
||||
'>'
|
||||
];
|
||||
var objects = this.getObjects(),
|
||||
markup = [
|
||||
'<g ',
|
||||
'style="', this.getSvgStyles(), '" ',
|
||||
'transform="', this.getSvgTransform(), '" ',
|
||||
'>'
|
||||
];
|
||||
|
||||
for (var i = 0, len = objects.length; i < len; i++) {
|
||||
markup.push(objects[i].toSVG(reviver));
|
||||
|
|
@ -174,9 +183,9 @@
|
|||
* @return {Boolean} true if all paths are of the same color (`fill`)
|
||||
*/
|
||||
isSameColor: function() {
|
||||
var firstPathFill = this.getObjects()[0].get('fill');
|
||||
var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
|
||||
return this.getObjects().every(function(path) {
|
||||
return path.get('fill') === firstPathFill;
|
||||
return (path.get('fill') || '').toLowerCase() === firstPathFill;
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
|
|
@ -28,6 +28,13 @@
|
|||
*/
|
||||
type: 'polygon',
|
||||
|
||||
/**
|
||||
* Points array
|
||||
* @type Array
|
||||
* @default
|
||||
*/
|
||||
points: null,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Array} points Array of points
|
||||
|
|
@ -138,7 +145,7 @@
|
|||
ctx.beginPath();
|
||||
for (var i = 0, len = this.points.length; i < len; i++) {
|
||||
p1 = this.points[i];
|
||||
p2 = this.points[i+1] || this.points[0];
|
||||
p2 = this.points[i + 1] || this.points[0];
|
||||
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
|
||||
}
|
||||
ctx.closePath();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
toFixed = fabric.util.toFixed;
|
||||
|
|
@ -25,6 +25,13 @@
|
|||
*/
|
||||
type: 'polyline',
|
||||
|
||||
/**
|
||||
* Points array
|
||||
* @type Array
|
||||
* @default
|
||||
*/
|
||||
points: null,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Array} points Array of points (where each point is an object with x and y)
|
||||
|
|
@ -122,7 +129,7 @@
|
|||
ctx.beginPath();
|
||||
for (var i = 0, len = this.points.length; i < len; i++) {
|
||||
p1 = this.points[i];
|
||||
p2 = this.points[i+1] || p1;
|
||||
p2 = this.points[i + 1] || p1;
|
||||
fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend;
|
||||
|
|
@ -101,13 +101,22 @@
|
|||
* @param ctx {CanvasRenderingContext2D} context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
var rx = this.rx || 0,
|
||||
ry = this.ry || 0,
|
||||
x = -this.width / 2,
|
||||
y = -this.height / 2,
|
||||
|
||||
// optimize 1x1 case (used in spray brush)
|
||||
if (this.width === 1 && this.height === 1) {
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
|
||||
ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
|
||||
w = this.width,
|
||||
h = this.height,
|
||||
isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
x = -w / 2,
|
||||
y = -h / 2,
|
||||
isInPathGroup = this.group && this.group.type === 'path-group',
|
||||
isRounded = rx !== 0 || ry !== 0,
|
||||
k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = isInPathGroup ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
|
|
@ -123,17 +132,20 @@
|
|||
-this.group.height / 2 + this.height / 2 + this.y);
|
||||
}
|
||||
|
||||
var isRounded = rx !== 0 || ry !== 0;
|
||||
ctx.moveTo(x + rx, y);
|
||||
|
||||
ctx.lineTo(x + w - rx, y);
|
||||
isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
|
||||
|
||||
ctx.lineTo(x + w, y + h - ry);
|
||||
isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
|
||||
|
||||
ctx.lineTo(x + rx, y + h);
|
||||
isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
|
||||
|
||||
ctx.lineTo(x, y + ry);
|
||||
isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
|
||||
|
||||
ctx.moveTo(x+rx, y);
|
||||
ctx.lineTo(x+w-rx, y);
|
||||
isRounded && ctx.quadraticCurveTo(x+w, y, x+w, y+ry, x+w, y+ry);
|
||||
ctx.lineTo(x+w, y+h-ry);
|
||||
isRounded && ctx.quadraticCurveTo(x+w,y+h,x+w-rx,y+h,x+w-rx,y+h);
|
||||
ctx.lineTo(x+rx,y+h);
|
||||
isRounded && ctx.quadraticCurveTo(x,y+h,x,y+h-ry,x,y+h-ry);
|
||||
ctx.lineTo(x,y+ry);
|
||||
isRounded && ctx.quadraticCurveTo(x,y,x+rx,y,x+rx,y);
|
||||
ctx.closePath();
|
||||
|
||||
this._renderFill(ctx);
|
||||
|
|
@ -145,16 +157,16 @@
|
|||
* @param ctx {CanvasRenderingContext2D} context to render on
|
||||
*/
|
||||
_renderDashedStroke: function(ctx) {
|
||||
var x = -this.width/2,
|
||||
y = -this.height/2,
|
||||
w = this.width,
|
||||
h = this.height;
|
||||
var x = -this.width / 2,
|
||||
y = -this.height / 2,
|
||||
w = this.width,
|
||||
h = this.height;
|
||||
|
||||
ctx.beginPath();
|
||||
fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
|
||||
fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
|
||||
ctx.closePath();
|
||||
},
|
||||
|
||||
|
|
@ -208,8 +220,7 @@
|
|||
'" width="', this.width, '" height="', this.height,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>'
|
||||
);
|
||||
'"/>');
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { }),
|
||||
extend = fabric.util.object.extend,
|
||||
|
|
@ -427,8 +427,8 @@
|
|||
|
||||
for (var i = 0, len = textLines.length; i < len; i++) {
|
||||
|
||||
var lineWidth = this._getLineWidth(ctx, textLines[i]);
|
||||
var lineLeftOffset = this._getLineLeftOffset(lineWidth);
|
||||
var lineWidth = this._getLineWidth(ctx, textLines[i]),
|
||||
lineLeftOffset = this._getLineLeftOffset(lineWidth);
|
||||
|
||||
this._boundaries.push({
|
||||
height: this.fontSize * this.lineHeight,
|
||||
|
|
@ -511,18 +511,18 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var lineWidth = ctx.measureText(line).width;
|
||||
var totalWidth = this.width;
|
||||
var lineWidth = ctx.measureText(line).width,
|
||||
totalWidth = this.width;
|
||||
|
||||
if (totalWidth > lineWidth) {
|
||||
// stretch the line
|
||||
var words = line.split(/\s+/);
|
||||
var wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width;
|
||||
var widthDiff = totalWidth - wordsWidth;
|
||||
var numSpaces = words.length - 1;
|
||||
var spaceWidth = widthDiff / numSpaces;
|
||||
var words = line.split(/\s+/),
|
||||
wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
|
||||
widthDiff = totalWidth - wordsWidth,
|
||||
numSpaces = words.length - 1,
|
||||
spaceWidth = widthDiff / numSpaces,
|
||||
leftOffset = 0;
|
||||
|
||||
var leftOffset = 0;
|
||||
for (var i = 0, len = words.length; i < len; i++) {
|
||||
this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
|
||||
leftOffset += ctx.measureText(words[i]).width + spaceWidth;
|
||||
|
|
@ -664,8 +664,8 @@
|
|||
|
||||
if (textLines[i] !== '') {
|
||||
|
||||
var lineWidth = this._getLineWidth(ctx, textLines[i]);
|
||||
var lineLeftOffset = this._getLineLeftOffset(lineWidth);
|
||||
var lineWidth = this._getLineWidth(ctx, textLines[i]),
|
||||
lineLeftOffset = this._getLineLeftOffset(lineWidth);
|
||||
|
||||
ctx.fillRect(
|
||||
this._getLeftOffset() + lineLeftOffset,
|
||||
|
|
@ -714,15 +714,15 @@
|
|||
if (!this.textDecoration) return;
|
||||
|
||||
// var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
|
||||
var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2;
|
||||
var _this = this;
|
||||
var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
|
||||
_this = this;
|
||||
|
||||
/** @ignore */
|
||||
function renderLinesAtOffset(offset) {
|
||||
for (var i = 0, len = textLines.length; i < len; i++) {
|
||||
|
||||
var lineWidth = _this._getLineWidth(ctx, textLines[i]);
|
||||
var lineLeftOffset = _this._getLineLeftOffset(lineWidth);
|
||||
var lineWidth = _this._getLineWidth(ctx, textLines[i]),
|
||||
lineLeftOffset = _this._getLineLeftOffset(lineWidth);
|
||||
|
||||
ctx.fillRect(
|
||||
_this._getLeftOffset() + lineLeftOffset,
|
||||
|
|
@ -766,8 +766,13 @@
|
|||
if (!this.visible) return;
|
||||
|
||||
ctx.save();
|
||||
var v = this.canvas.viewportTransform;
|
||||
var v = this.getViewportTransform();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
var m = this.transformMatrix;
|
||||
if (m && (!this.group || this.group.type === 'path-group')) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
this._render(ctx);
|
||||
ctx.restore();
|
||||
|
||||
|
|
@ -953,7 +958,7 @@
|
|||
(i === 0 || this.useNative ? 'y' : 'dy'), '="',
|
||||
toFixed(this.useNative
|
||||
? ((lineHeight * i) - this.height / 2)
|
||||
: (lineHeight * lineTopOffsetMultiplier), 2) , '" ',
|
||||
: (lineHeight * lineTopOffsetMultiplier), 2), '" ',
|
||||
// doing this on <tspan> elements since setting opacity
|
||||
// on containing <text> one doesn't work in Illustrator
|
||||
this._getFillAttributes(this.fill), '>',
|
||||
|
|
@ -1048,7 +1053,14 @@
|
|||
* @see: http://www.w3.org/TR/SVG/text.html#TextElement
|
||||
*/
|
||||
fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
|
||||
'x y font-family font-style font-weight font-size text-decoration'.split(' '));
|
||||
'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
|
||||
|
||||
/**
|
||||
* Default SVG font size
|
||||
* @static
|
||||
* @memberOf fabric.Text
|
||||
*/
|
||||
fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
|
||||
|
|
@ -1066,6 +1078,20 @@
|
|||
var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
|
||||
options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
|
||||
|
||||
if ('dx' in parsedAttributes) {
|
||||
options.left += parsedAttributes.dx;
|
||||
}
|
||||
if ('dy' in parsedAttributes) {
|
||||
options.top += parsedAttributes.dy;
|
||||
}
|
||||
if (!('fontSize' in options)) {
|
||||
options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
|
||||
}
|
||||
|
||||
if (!options.originX) {
|
||||
options.originX = 'center';
|
||||
}
|
||||
|
||||
var text = new fabric.Text(element.textContent, options);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function(global) {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
|
|
@ -81,13 +81,13 @@
|
|||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup(),
|
||||
widthBy2 = this.width / 2,
|
||||
heightBy2 = this.height / 2;
|
||||
|
||||
var points = [
|
||||
-widthBy2 + " " + heightBy2,
|
||||
"0 " + -heightBy2,
|
||||
widthBy2 + " " + heightBy2
|
||||
].join(",");
|
||||
heightBy2 = this.height / 2,
|
||||
points = [
|
||||
-widthBy2 + ' ' + heightBy2,
|
||||
'0 ' + -heightBy2,
|
||||
widthBy2 + ' ' + heightBy2
|
||||
]
|
||||
.join(',');
|
||||
|
||||
markup.push(
|
||||
'<polygon ',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function () {
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
if (fabric.StaticCanvas) {
|
||||
fabric.warn('fabric.StaticCanvas is already defined.');
|
||||
|
|
@ -124,7 +124,14 @@
|
|||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
allowTouchScrolling: false,
|
||||
allowTouchScrolling: false,
|
||||
|
||||
/**
|
||||
* Indicates whether this canvas will use image smoothing, this is on by default in browsers
|
||||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
imageSmoothingEnabled: true,
|
||||
|
||||
/**
|
||||
* The transformation (in the format of Canvas transform) which focuses the viewport
|
||||
|
|
@ -151,6 +158,7 @@
|
|||
|
||||
this._createLowerCanvas(el);
|
||||
this._initOptions(options);
|
||||
this._setImageSmoothing();
|
||||
|
||||
if (options.overlayImage) {
|
||||
this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
|
||||
|
|
@ -310,6 +318,20 @@
|
|||
return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
|
||||
*/
|
||||
_setImageSmoothing: function(){
|
||||
var ctx = this.getContext();
|
||||
|
||||
ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
|
||||
ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
|
||||
ctx.mozImageSmoothingEnabled = this.imageSmoothingEnabled;
|
||||
ctx.msImageSmoothingEnabled = this.imageSmoothingEnabled;
|
||||
ctx.oImageSmoothingEnabled = this.imageSmoothingEnabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
|
||||
|
|
@ -397,11 +419,14 @@
|
|||
this[prop] = options[prop];
|
||||
}
|
||||
|
||||
this.width = parseInt(this.lowerCanvasEl.width, 10) || 0;
|
||||
this.height = parseInt(this.lowerCanvasEl.height, 10) || 0;
|
||||
this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
|
||||
this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
|
||||
|
||||
if (!this.lowerCanvasEl.style) return;
|
||||
|
||||
this.lowerCanvasEl.width = this.width;
|
||||
this.lowerCanvasEl.height = this.height;
|
||||
|
||||
this.lowerCanvasEl.style.width = this.width + 'px';
|
||||
this.lowerCanvasEl.style.height = this.height + 'px';
|
||||
},
|
||||
|
|
@ -723,8 +748,8 @@
|
|||
*/
|
||||
renderAll: function (allOnTop) {
|
||||
|
||||
var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'];
|
||||
var activeGroup = this.getActiveGroup();
|
||||
var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
|
||||
activeGroup = this.getActiveGroup();
|
||||
|
||||
if (this.contextTop && this.selection && !this._groupSelector) {
|
||||
this.clearContext(this.contextTop);
|
||||
|
|
@ -765,12 +790,21 @@
|
|||
* @param {fabric.Group} activeGroup
|
||||
*/
|
||||
_renderObjects: function(ctx, activeGroup) {
|
||||
for (var i = 0, length = this._objects.length; i < length; ++i) {
|
||||
if (!activeGroup ||
|
||||
(activeGroup && this._objects[i] && !activeGroup.contains(this._objects[i]))) {
|
||||
var i, length;
|
||||
|
||||
// fast path
|
||||
if (!activeGroup) {
|
||||
for (i = 0, length = this._objects.length; i < length; ++i) {
|
||||
this._draw(ctx, this._objects[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0, length = this._objects.length; i < length; ++i) {
|
||||
if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
|
||||
this._draw(ctx, this._objects[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -859,9 +893,7 @@
|
|||
activeGroup.render(ctx);
|
||||
}
|
||||
|
||||
if (this.overlayImage) {
|
||||
ctx.drawImage(this.overlayImage, this.overlayImageLeft, this.overlayImageTop);
|
||||
}
|
||||
this._renderOverlay(ctx);
|
||||
|
||||
this.fire('after:render');
|
||||
|
||||
|
|
@ -978,11 +1010,20 @@
|
|||
fabric.util.populateWithProperties(this, data, propertiesToInclude);
|
||||
|
||||
if (activeGroup) {
|
||||
this.setActiveGroup(new fabric.Group(activeGroup.getObjects()));
|
||||
this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
|
||||
originX: 'center',
|
||||
originY: 'center'
|
||||
}));
|
||||
activeGroup.forEachObject(function(o) {
|
||||
o.set('active', true);
|
||||
});
|
||||
|
||||
if (this._currentTransform) {
|
||||
this._currentTransform.target = this.getActiveGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
|
|
@ -1119,7 +1160,7 @@
|
|||
'width="', (options.viewBox ? options.viewBox.width : this.width), '" ',
|
||||
'height="', (options.viewBox ? options.viewBox.height : this.height), '" ',
|
||||
(this.backgroundColor && !this.backgroundColor.toLive
|
||||
? 'style="background-color: ' + this.backgroundColor +'" '
|
||||
? 'style="background-color: ' + this.backgroundColor + '" '
|
||||
: null),
|
||||
(options.viewBox
|
||||
? 'viewBox="' +
|
||||
|
|
@ -1251,7 +1292,7 @@
|
|||
newIdx = idx;
|
||||
|
||||
// traverse down the stack looking for the nearest intersecting object
|
||||
for (var i=idx-1; i>=0; --i) {
|
||||
for (var i = idx - 1; i >= 0; --i) {
|
||||
|
||||
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
|
||||
object.isContainedWithinObject(this._objects[i]) ||
|
||||
|
|
@ -1281,7 +1322,7 @@
|
|||
var idx = this._objects.indexOf(object);
|
||||
|
||||
// if object is not on top of stack (last item in an array)
|
||||
if (idx !== this._objects.length-1) {
|
||||
if (idx !== this._objects.length - 1) {
|
||||
var newIdx = this._findNewUpperIndex(object, idx, intersecting);
|
||||
|
||||
removeFromArray(this._objects, object);
|
||||
|
|
@ -1314,7 +1355,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
newIdx = idx+1;
|
||||
newIdx = idx + 1;
|
||||
}
|
||||
|
||||
return newIdx;
|
||||
|
|
@ -1349,7 +1390,7 @@
|
|||
* @return {String} string representation of an instance
|
||||
*/
|
||||
toString: function () {
|
||||
return '#<fabric.Canvas (' + this.complexity() + '): '+
|
||||
return '#<fabric.Canvas (' + this.complexity() + '): ' +
|
||||
'{ objects: ' + this.getObjects().length + ' }>';
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
(function() {
|
||||
|
||||
function normalize(a, c, p, s) {
|
||||
if (a < Math.abs(c)) { a=c; s=p/4; }
|
||||
else s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
if (a < Math.abs(c)) {
|
||||
a = c;
|
||||
s = p / 4;
|
||||
}
|
||||
else {
|
||||
s = p / (2 * Math.PI) * Math.asin(c / a);
|
||||
}
|
||||
return { a: a, c: c, p: p, s: s };
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +22,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutCubic(t, b, c, d) {
|
||||
return c*((t=t/d-1)*t*t + 1) + b;
|
||||
return c * ((t = t / d - 1) * t * t + 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,8 +31,10 @@
|
|||
*/
|
||||
function easeInOutCubic(t, b, c, d) {
|
||||
t /= d/2;
|
||||
if (t < 1) return c/2*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t + 2) + b;
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t * t + b;
|
||||
}
|
||||
return c / 2 * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +42,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInQuart(t, b, c, d) {
|
||||
return c*(t/=d)*t*t*t + b;
|
||||
return c * (t /= d) * t * t * t + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +50,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutQuart(t, b, c, d) {
|
||||
return -c * ((t=t/d-1)*t*t*t - 1) + b;
|
||||
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,9 +58,11 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutQuart(t, b, c, d) {
|
||||
t /= d/2;
|
||||
if (t < 1) return c/2*t*t*t*t + b;
|
||||
return -c/2 * ((t-=2)*t*t*t - 2) + b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t * t * t + b;
|
||||
}
|
||||
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,7 +70,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInQuint(t, b, c, d) {
|
||||
return c*(t/=d)*t*t*t*t + b;
|
||||
return c * (t /= d) * t * t * t * t + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,7 +78,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutQuint(t, b, c, d) {
|
||||
return c*((t=t/d-1)*t*t*t*t + 1) + b;
|
||||
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -77,9 +86,11 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutQuint(t, b, c, d) {
|
||||
t /= d/2;
|
||||
if (t < 1) return c/2*t*t*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t*t*t + 2) + b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t * t * t * t + b;
|
||||
}
|
||||
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -87,7 +98,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInSine(t, b, c, d) {
|
||||
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
|
||||
return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +106,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutSine(t, b, c, d) {
|
||||
return c * Math.sin(t/d * (Math.PI/2)) + b;
|
||||
return c * Math.sin(t / d * (Math.PI / 2)) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -103,7 +114,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutSine(t, b, c, d) {
|
||||
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
|
||||
return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,7 +122,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInExpo(t, b, c, d) {
|
||||
return (t===0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
|
||||
return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -119,7 +130,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutExpo(t, b, c, d) {
|
||||
return (t===d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
|
||||
return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -127,11 +138,17 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutExpo(t, b, c, d) {
|
||||
if (t===0) return b;
|
||||
if (t===d) return b+c;
|
||||
t /= d/2;
|
||||
if (t < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
|
||||
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
if (t === d) {
|
||||
return b + c;
|
||||
}
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
|
||||
}
|
||||
return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -139,7 +156,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInCirc(t, b, c, d) {
|
||||
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
|
||||
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,7 +164,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutCirc(t, b, c, d) {
|
||||
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
|
||||
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,9 +172,11 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutCirc(t, b, c, d) {
|
||||
t /= d/2;
|
||||
if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
|
||||
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
|
||||
}
|
||||
return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,11 +184,17 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInElastic(t, b, c, d) {
|
||||
var s=1.70158;var p=0;var a=c;
|
||||
if (t===0) return b;
|
||||
var s = 1.70158, p = 0, a = c;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
t /= d;
|
||||
if (t===1) return b+c;
|
||||
if (!p) p=d*0.3;
|
||||
if (t === 1) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * 0.3;
|
||||
}
|
||||
var opts = normalize(a, c, p, s);
|
||||
return -elastic(opts, t, d) + b;
|
||||
}
|
||||
|
|
@ -179,13 +204,19 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutElastic(t, b, c, d) {
|
||||
var s=1.70158;var p=0;var a=c;
|
||||
if (t===0) return b;
|
||||
var s = 1.70158, p = 0, a = c;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
t /= d;
|
||||
if (t===1) return b+c;
|
||||
if (!p) p=d*0.3;
|
||||
if (t === 1) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * 0.3;
|
||||
}
|
||||
var opts = normalize(a, c, p, s);
|
||||
return opts.a*Math.pow(2,-10*t) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p ) + opts.c + b;
|
||||
return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -193,14 +224,22 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutElastic(t, b, c, d) {
|
||||
var s=1.70158;var p=0;var a=c;
|
||||
if (t===0) return b;
|
||||
t /= d/2;
|
||||
if (t===2) return b+c;
|
||||
if (!p) p=d*(0.3*1.5);
|
||||
var s = 1.70158, p = 0, a = c;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
t /= d / 2;
|
||||
if (t === 2) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * (0.3 * 1.5);
|
||||
}
|
||||
var opts = normalize(a, c, p, s);
|
||||
if (t < 1) return -0.5 * elastic(opts, t, d) + b;
|
||||
return opts.a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-opts.s)*(2*Math.PI)/opts.p )*0.5 + opts.c + b;
|
||||
if (t < 1) {
|
||||
return -0.5 * elastic(opts, t, d) + b;
|
||||
}
|
||||
return opts.a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -208,8 +247,10 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInBack(t, b, c, d, s) {
|
||||
if (s === undefined) s = 1.70158;
|
||||
return c*(t/=d)*t*((s+1)*t - s) + b;
|
||||
if (s === undefined) {
|
||||
s = 1.70158;
|
||||
}
|
||||
return c * (t /= d) * t * ((s + 1) * t - s) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -217,8 +258,10 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutBack(t, b, c, d, s) {
|
||||
if (s === undefined) s = 1.70158;
|
||||
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
|
||||
if (s === undefined) {
|
||||
s = 1.70158;
|
||||
}
|
||||
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -226,10 +269,14 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutBack(t, b, c, d, s) {
|
||||
if (s === undefined) s = 1.70158;
|
||||
t /= d/2;
|
||||
if (t < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
|
||||
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
|
||||
if (s === undefined) {
|
||||
s = 1.70158;
|
||||
}
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
|
||||
}
|
||||
return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -237,7 +284,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInBounce(t, b, c, d) {
|
||||
return c - easeOutBounce (d-t, 0, c, d) + b;
|
||||
return c - easeOutBounce (d - t, 0, c, d) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -245,14 +292,17 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeOutBounce(t, b, c, d) {
|
||||
if ((t/=d) < (1/2.75)) {
|
||||
return c*(7.5625*t*t) + b;
|
||||
} else if (t < (2/2.75)) {
|
||||
return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
|
||||
} else if (t < (2.5/2.75)) {
|
||||
return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
|
||||
} else {
|
||||
return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
|
||||
if ((t /= d) < (1 / 2.75)) {
|
||||
return c * (7.5625 * t * t) + b;
|
||||
}
|
||||
else if (t < (2/2.75)) {
|
||||
return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
|
||||
}
|
||||
else if (t < (2.5/2.75)) {
|
||||
return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
|
||||
}
|
||||
else {
|
||||
return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,8 +311,10 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
function easeInOutBounce(t, b, c, d) {
|
||||
if (t < d/2) return easeInBounce (t*2, 0, c, d) * 0.5 + b;
|
||||
return easeOutBounce (t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
|
||||
if (t < d / 2) {
|
||||
return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
|
||||
}
|
||||
return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -277,7 +329,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
easeInQuad: function(t, b, c, d) {
|
||||
return c*(t/=d)*t + b;
|
||||
return c * (t /= d) * t + b;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -285,7 +337,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
easeOutQuad: function(t, b, c, d) {
|
||||
return -c *(t/=d)*(t-2) + b;
|
||||
return -c * (t /= d) * (t - 2) + b;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -293,9 +345,11 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
easeInOutQuad: function(t, b, c, d) {
|
||||
t /= (d/2);
|
||||
if (t < 1) return c/2*t*t + b;
|
||||
return -c/2 * ((--t)*(t-2) - 1) + b;
|
||||
t /= (d / 2);
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b;
|
||||
}
|
||||
return -c / 2 * ((--t) * (t - 2) - 1) + b;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -303,7 +357,7 @@
|
|||
* @memberOf fabric.util.ease
|
||||
*/
|
||||
easeInCubic: function(t, b, c, d) {
|
||||
return c*(t/=d)*t*t + b;
|
||||
return c * (t /= d) * t * t + b;
|
||||
},
|
||||
|
||||
easeOutCubic: easeOutCubic,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* @param {Number} [options.endValue=100] Ending value
|
||||
* @param {Number} [options.byValue=100] Value to modify the property by
|
||||
* @param {Function} [options.easing] Easing function
|
||||
* @param {Number} [options.duration=500] Duration of change
|
||||
* @param {Number} [options.duration=500] Duration of change (in ms)
|
||||
*/
|
||||
function animate(options) {
|
||||
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
finish = start + duration, time,
|
||||
onChange = options.onChange || function() { },
|
||||
abort = options.abort || function() { return false; },
|
||||
easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t/d * (Math.PI/2)) + c + b;},
|
||||
easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
|
||||
startValue = 'startValue' in options ? options.startValue : 0,
|
||||
endValue = 'endValue' in options ? options.endValue : 100,
|
||||
byValue = options.byValue || endValue - startValue;
|
||||
|
|
@ -62,9 +62,9 @@
|
|||
* @param {Function} callback Callback to invoke
|
||||
* @param {DOMElement} element optional Element to associate with animation
|
||||
*/
|
||||
var requestAnimFrame = function() {
|
||||
function requestAnimFrame() {
|
||||
return _requestAnimFrame.apply(fabric.window, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
fabric.util.animate = animate;
|
||||
fabric.util.requestAnimFrame = requestAnimFrame;
|
||||
|
|
|
|||
138
src/util/arc.js
138
src/util/arc.js
|
|
@ -14,37 +14,43 @@
|
|||
return arcToSegmentsCache[argsString];
|
||||
}
|
||||
|
||||
var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y);
|
||||
var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y),
|
||||
|
||||
var d = (coords.x1-coords.x0) * (coords.x1-coords.x0) +
|
||||
(coords.y1-coords.y0) * (coords.y1-coords.y0);
|
||||
d = (coords.x1 - coords.x0) * (coords.x1 - coords.x0) +
|
||||
(coords.y1 - coords.y0) * (coords.y1 - coords.y0),
|
||||
|
||||
var sfactor_sq = 1 / d - 0.25;
|
||||
if (sfactor_sq < 0) sfactor_sq = 0;
|
||||
sfactorSq = 1 / d - 0.25;
|
||||
|
||||
var sfactor = Math.sqrt(sfactor_sq);
|
||||
if (sweep === large) sfactor = -sfactor;
|
||||
|
||||
var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1-coords.y0);
|
||||
var yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1-coords.x0);
|
||||
|
||||
var th0 = Math.atan2(coords.y0-yc, coords.x0-xc);
|
||||
var th1 = Math.atan2(coords.y1-yc, coords.x1-xc);
|
||||
|
||||
var th_arc = th1-th0;
|
||||
if (th_arc < 0 && sweep === 1) {
|
||||
th_arc += 2*Math.PI;
|
||||
}
|
||||
else if (th_arc > 0 && sweep === 0) {
|
||||
th_arc -= 2 * Math.PI;
|
||||
if (sfactorSq < 0) {
|
||||
sfactorSq = 0;
|
||||
}
|
||||
|
||||
var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
|
||||
var result = [];
|
||||
for (var i=0; i<segments; i++) {
|
||||
var th2 = th0 + i * th_arc / segments;
|
||||
var th3 = th0 + (i+1) * th_arc / segments;
|
||||
result[i] = [xc, yc, th2, th3, rx, ry, coords.sin_th, coords.cos_th];
|
||||
var sfactor = Math.sqrt(sfactorSq);
|
||||
if (sweep === large) {
|
||||
sfactor = -sfactor;
|
||||
}
|
||||
|
||||
var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1 - coords.y0),
|
||||
yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1 - coords.x0),
|
||||
th0 = Math.atan2(coords.y0 - yc, coords.x0 - xc),
|
||||
th1 = Math.atan2(coords.y1 - yc, coords.x1 - xc),
|
||||
thArc = th1 - th0;
|
||||
|
||||
if (thArc < 0 && sweep === 1) {
|
||||
thArc += 2 * Math.PI;
|
||||
}
|
||||
else if (thArc > 0 && sweep === 0) {
|
||||
thArc -= 2 * Math.PI;
|
||||
}
|
||||
|
||||
var segments = Math.ceil(Math.abs(thArc / (Math.PI * 0.5 + 0.001))),
|
||||
result = [];
|
||||
|
||||
for (var i = 0; i < segments; i++) {
|
||||
var th2 = th0 + i * thArc / segments,
|
||||
th3 = th0 + (i + 1) * thArc / segments;
|
||||
|
||||
result[i] = [xc, yc, th2, th3, rx, ry, coords.sinTh, coords.cosTh];
|
||||
}
|
||||
|
||||
arcToSegmentsCache[argsString] = result;
|
||||
|
|
@ -53,56 +59,59 @@
|
|||
|
||||
function getXYCoords(rotateX, rx, ry, ox, oy, x, y) {
|
||||
|
||||
var th = rotateX * (Math.PI/180);
|
||||
var sin_th = Math.sin(th);
|
||||
var cos_th = Math.cos(th);
|
||||
var th = rotateX * (Math.PI / 180),
|
||||
sinTh = Math.sin(th),
|
||||
cosTh = Math.cos(th);
|
||||
|
||||
rx = Math.abs(rx);
|
||||
ry = Math.abs(ry);
|
||||
var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
||||
var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
||||
var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
|
||||
|
||||
var px = cosTh * (ox - x) * 0.5 + sinTh * (oy - y) * 0.5,
|
||||
py = cosTh * (oy - y) * 0.5 - sinTh * (ox - x) * 0.5,
|
||||
pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
|
||||
|
||||
if (pl > 1) {
|
||||
pl = Math.sqrt(pl);
|
||||
rx *= pl;
|
||||
ry *= pl;
|
||||
}
|
||||
|
||||
var a00 = cos_th / rx;
|
||||
var a01 = sin_th / rx;
|
||||
var a10 = (-sin_th) / ry;
|
||||
var a11 = (cos_th) / ry;
|
||||
var a00 = cosTh / rx,
|
||||
a01 = sinTh / rx,
|
||||
a10 = (-sinTh) / ry,
|
||||
a11 = (cosTh) / ry;
|
||||
|
||||
return {
|
||||
x0: a00 * ox + a01 * oy,
|
||||
y0: a10 * ox + a11 * oy,
|
||||
x1: a00 * x + a01 * y,
|
||||
y1: a10 * x + a11 * y,
|
||||
sin_th: sin_th,
|
||||
cos_th: cos_th
|
||||
sinTh: sinTh,
|
||||
cosTh: cosTh
|
||||
};
|
||||
}
|
||||
|
||||
function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
||||
function segmentToBezier(cx, cy, th0, th1, rx, ry, sinTh, cosTh) {
|
||||
argsString = _join.call(arguments);
|
||||
|
||||
if (segmentToBezierCache[argsString]) {
|
||||
return segmentToBezierCache[argsString];
|
||||
}
|
||||
|
||||
var a00 = cos_th * rx;
|
||||
var a01 = -sin_th * ry;
|
||||
var a10 = sin_th * rx;
|
||||
var a11 = cos_th * ry;
|
||||
var a00 = cosTh * rx,
|
||||
a01 = -sinTh * ry,
|
||||
a10 = sinTh * rx,
|
||||
a11 = cosTh * ry,
|
||||
thHalf = 0.5 * (th1 - th0),
|
||||
t = (8 / 3) * Math.sin(thHalf * 0.5) *
|
||||
Math.sin(thHalf * 0.5) / Math.sin(thHalf),
|
||||
|
||||
var th_half = 0.5 * (th1 - th0);
|
||||
var t = (8/3) * Math.sin(th_half * 0.5) *
|
||||
Math.sin(th_half * 0.5) / Math.sin(th_half);
|
||||
|
||||
var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
|
||||
var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
|
||||
var x3 = cx + Math.cos(th1);
|
||||
var y3 = cy + Math.sin(th1);
|
||||
var x2 = x3 + t * Math.sin(th1);
|
||||
var y2 = y3 - t * Math.cos(th1);
|
||||
x1 = cx + Math.cos(th0) - t * Math.sin(th0),
|
||||
y1 = cy + Math.sin(th0) + t * Math.cos(th0),
|
||||
x3 = cx + Math.cos(th1),
|
||||
y3 = cy + Math.sin(th1),
|
||||
x2 = x3 + t * Math.sin(th1),
|
||||
y2 = y3 - t * Math.cos(th1);
|
||||
|
||||
segmentToBezierCache[argsString] = [
|
||||
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
|
||||
|
|
@ -121,17 +130,18 @@
|
|||
* @param {Array} coords
|
||||
*/
|
||||
fabric.util.drawArc = function(ctx, x, y, coords) {
|
||||
var rx = coords[0];
|
||||
var ry = coords[1];
|
||||
var rot = coords[2];
|
||||
var large = coords[3];
|
||||
var sweep = coords[4];
|
||||
var ex = coords[5];
|
||||
var ey = coords[6];
|
||||
var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
|
||||
for (var i=0; i<segs.length; i++) {
|
||||
var bez = segmentToBezier.apply(this, segs[i]);
|
||||
ctx.bezierCurveTo.apply(ctx, bez);
|
||||
var rx = coords[0],
|
||||
ry = coords[1],
|
||||
rot = coords[2],
|
||||
large = coords[3],
|
||||
sweep = coords[4],
|
||||
ex = coords[5],
|
||||
ey = coords[6],
|
||||
segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
|
||||
|
||||
for (var i = 0; i < segs.length; i++) {
|
||||
var bez = segmentToBezier.apply(this, segs[i]);
|
||||
ctx.bezierCurveTo.apply(ctx, bez);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -13,15 +13,16 @@
|
|||
}
|
||||
return true;
|
||||
}
|
||||
var getUniqueId = (function () {
|
||||
var uid = 0;
|
||||
return function (element) {
|
||||
return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
|
||||
};
|
||||
})();
|
||||
|
||||
/** @ignore */
|
||||
var getElement, setElement;
|
||||
var getElement,
|
||||
setElement,
|
||||
getUniqueId = (function () {
|
||||
var uid = 0;
|
||||
return function (element) {
|
||||
return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
|
||||
};
|
||||
})();
|
||||
|
||||
(function () {
|
||||
var elements = { };
|
||||
|
|
@ -177,9 +178,9 @@
|
|||
event || (event = fabric.window.event);
|
||||
|
||||
var element = event.target ||
|
||||
(typeof event.srcElement !== unknown ? event.srcElement : null);
|
||||
(typeof event.srcElement !== unknown ? event.srcElement : null),
|
||||
|
||||
var scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
|
||||
scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
|
||||
|
||||
return {
|
||||
x: pointerX(event) + scroll.left,
|
||||
|
|
@ -192,9 +193,9 @@
|
|||
// is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
|
||||
// need to investigate later
|
||||
return (typeof event.clientX !== unknown ? event.clientX : 0);
|
||||
};
|
||||
},
|
||||
|
||||
var pointerY = function(event) {
|
||||
pointerY = function(event) {
|
||||
return (typeof event.clientY !== unknown ? event.clientY : 0);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,21 +12,21 @@
|
|||
return typeof id === 'string' ? fabric.document.getElementById(id) : id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array-like object (e.g. arguments or NodeList) to an array
|
||||
* @memberOf fabric.util
|
||||
* @param {Object} arrayLike
|
||||
* @return {Array}
|
||||
*/
|
||||
var toArray = function(arrayLike) {
|
||||
return _slice.call(arrayLike, 0);
|
||||
};
|
||||
var sliceCanConvertNodelists,
|
||||
/**
|
||||
* Converts an array-like object (e.g. arguments or NodeList) to an array
|
||||
* @memberOf fabric.util
|
||||
* @param {Object} arrayLike
|
||||
* @return {Array}
|
||||
*/
|
||||
toArray = function(arrayLike) {
|
||||
return _slice.call(arrayLike, 0);
|
||||
};
|
||||
|
||||
var sliceCanConvertNodelists;
|
||||
try {
|
||||
sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
|
||||
}
|
||||
catch(err) { }
|
||||
catch (err) { }
|
||||
|
||||
if (!sliceCanConvertNodelists) {
|
||||
toArray = function(arrayLike) {
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
* @param {String} className Class to add to an element
|
||||
*/
|
||||
function addClass(element, className) {
|
||||
if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
|
||||
if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
|
||||
element.className += (element.className ? ' ' : '') + className;
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,14 @@
|
|||
wrapper.appendChild(element);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns element scroll offsets
|
||||
* @memberOf fabric.util
|
||||
* @param {HTMLElement} element Element to operate on
|
||||
* @param {HTMLElement} upperCanvasEl Upper canvas element
|
||||
* @return {Object} Object with left/top values
|
||||
*/
|
||||
function getScrollLeftTop(element, upperCanvasEl) {
|
||||
|
||||
var firstFixedAncestor,
|
||||
|
|
@ -142,19 +149,19 @@
|
|||
*/
|
||||
function getElementOffset(element) {
|
||||
var docElem,
|
||||
box = {left: 0, top: 0},
|
||||
doc = element && element.ownerDocument,
|
||||
offset = {left: 0, top: 0},
|
||||
box = { left: 0, top: 0 },
|
||||
offset = { left: 0, top: 0 },
|
||||
scrollLeftTop,
|
||||
offsetAttributes = {
|
||||
'borderLeftWidth': 'left',
|
||||
'borderTopWidth': 'top',
|
||||
'paddingLeft': 'left',
|
||||
'paddingTop': 'top'
|
||||
borderLeftWidth: 'left',
|
||||
borderTopWidth: 'top',
|
||||
paddingLeft: 'left',
|
||||
paddingTop: 'top'
|
||||
};
|
||||
|
||||
if (!doc){
|
||||
return {left: 0, top: 0};
|
||||
if (!doc) {
|
||||
return { left: 0, top: 0 };
|
||||
}
|
||||
|
||||
for (var attr in offsetAttributes) {
|
||||
|
|
@ -162,7 +169,7 @@
|
|||
}
|
||||
|
||||
docElem = doc.documentElement;
|
||||
if ( typeof element.getBoundingClientRect !== "undefined" ) {
|
||||
if ( typeof element.getBoundingClientRect !== 'undefined' ) {
|
||||
box = element.getBoundingClientRect();
|
||||
}
|
||||
|
||||
|
|
@ -181,33 +188,33 @@
|
|||
* @param {String} attr Style attribute to get for element
|
||||
* @return {String} Style attribute value of the given element.
|
||||
*/
|
||||
function getElementStyle(element, attr) {
|
||||
if (!element.style) {
|
||||
element.style = { };
|
||||
}
|
||||
|
||||
if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
|
||||
var getElementStyle;
|
||||
if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
|
||||
getElementStyle = function(element, attr) {
|
||||
return fabric.document.defaultView.getComputedStyle(element, null)[attr];
|
||||
}
|
||||
else {
|
||||
};
|
||||
}
|
||||
else {
|
||||
getElementStyle = function(element, attr) {
|
||||
var value = element.style[attr];
|
||||
if (!value && element.currentStyle) value = element.currentStyle[attr];
|
||||
if (!value && element.currentStyle) {
|
||||
value = element.currentStyle[attr];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
(function () {
|
||||
var style = fabric.document.documentElement.style;
|
||||
|
||||
var selectProp = 'userSelect' in style
|
||||
? 'userSelect'
|
||||
: 'MozUserSelect' in style
|
||||
? 'MozUserSelect'
|
||||
: 'WebkitUserSelect' in style
|
||||
? 'WebkitUserSelect'
|
||||
: 'KhtmlUserSelect' in style
|
||||
? 'KhtmlUserSelect'
|
||||
: '';
|
||||
var style = fabric.document.documentElement.style,
|
||||
selectProp = 'userSelect' in style
|
||||
? 'userSelect'
|
||||
: 'MozUserSelect' in style
|
||||
? 'MozUserSelect'
|
||||
: 'WebkitUserSelect' in style
|
||||
? 'WebkitUserSelect'
|
||||
: 'KhtmlUserSelect' in style
|
||||
? 'KhtmlUserSelect'
|
||||
: '';
|
||||
|
||||
/**
|
||||
* Makes element unselectable
|
||||
|
|
@ -260,7 +267,7 @@
|
|||
* @param {Function} callback Callback to execute when script is finished loading
|
||||
*/
|
||||
function getScript(url, callback) {
|
||||
var headEl = fabric.document.getElementsByTagName("head")[0],
|
||||
var headEl = fabric.document.getElementsByTagName('head')[0],
|
||||
scriptEl = fabric.document.createElement('script'),
|
||||
loading = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
var makeXHR = (function() {
|
||||
var factories = [
|
||||
function() { return new ActiveXObject("Microsoft.XMLHTTP"); },
|
||||
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
|
||||
function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); },
|
||||
function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
|
||||
function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
|
||||
function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
|
||||
function() { return new XMLHttpRequest(); }
|
||||
];
|
||||
for (var i = factories.length; i--; ) {
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@
|
|||
* @private
|
||||
*/
|
||||
function find(array, byProperty, condition) {
|
||||
if (!array || array.length === 0) return undefined;
|
||||
if (!array || array.length === 0) return;
|
||||
|
||||
var i = array.length - 1,
|
||||
result = byProperty ? array[i][byProperty] : array[i];
|
||||
|
|
|
|||
|
|
@ -1,50 +1,50 @@
|
|||
(function() {
|
||||
|
||||
var slice = Array.prototype.slice, emptyFunction = function() { };
|
||||
var slice = Array.prototype.slice, emptyFunction = function() { },
|
||||
|
||||
var IS_DONTENUM_BUGGY = (function(){
|
||||
for (var p in { toString: 1 }) {
|
||||
if (p === 'toString') return false;
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
IS_DONTENUM_BUGGY = (function(){
|
||||
for (var p in { toString: 1 }) {
|
||||
if (p === 'toString') return false;
|
||||
}
|
||||
return true;
|
||||
})(),
|
||||
|
||||
/** @ignore */
|
||||
var addMethods = function(klass, source, parent) {
|
||||
for (var property in source) {
|
||||
/** @ignore */
|
||||
addMethods = function(klass, source, parent) {
|
||||
for (var property in source) {
|
||||
|
||||
if (property in klass.prototype &&
|
||||
typeof klass.prototype[property] === 'function' &&
|
||||
(source[property] + '').indexOf('callSuper') > -1) {
|
||||
if (property in klass.prototype &&
|
||||
typeof klass.prototype[property] === 'function' &&
|
||||
(source[property] + '').indexOf('callSuper') > -1) {
|
||||
|
||||
klass.prototype[property] = (function(property) {
|
||||
return function() {
|
||||
klass.prototype[property] = (function(property) {
|
||||
return function() {
|
||||
|
||||
var superclass = this.constructor.superclass;
|
||||
this.constructor.superclass = parent;
|
||||
var returnValue = source[property].apply(this, arguments);
|
||||
this.constructor.superclass = superclass;
|
||||
var superclass = this.constructor.superclass;
|
||||
this.constructor.superclass = parent;
|
||||
var returnValue = source[property].apply(this, arguments);
|
||||
this.constructor.superclass = superclass;
|
||||
|
||||
if (property !== 'initialize') {
|
||||
return returnValue;
|
||||
if (property !== 'initialize') {
|
||||
return returnValue;
|
||||
}
|
||||
};
|
||||
})(property);
|
||||
}
|
||||
else {
|
||||
klass.prototype[property] = source[property];
|
||||
}
|
||||
|
||||
if (IS_DONTENUM_BUGGY) {
|
||||
if (source.toString !== Object.prototype.toString) {
|
||||
klass.prototype.toString = source.toString;
|
||||
}
|
||||
};
|
||||
})(property);
|
||||
}
|
||||
else {
|
||||
klass.prototype[property] = source[property];
|
||||
}
|
||||
|
||||
if (IS_DONTENUM_BUGGY) {
|
||||
if (source.toString !== Object.prototype.toString) {
|
||||
klass.prototype.toString = source.toString;
|
||||
if (source.valueOf !== Object.prototype.valueOf) {
|
||||
klass.prototype.valueOf = source.valueOf;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (source.valueOf !== Object.prototype.valueOf) {
|
||||
klass.prototype.valueOf = source.valueOf;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function Subclass() { }
|
||||
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@
|
|||
* @return {Function}
|
||||
*/
|
||||
Function.prototype.bind = function(thisArg) {
|
||||
var fn = this, args = slice.call(arguments, 1), bound;
|
||||
var _this = this, args = slice.call(arguments, 1), bound;
|
||||
if (args.length) {
|
||||
bound = function() {
|
||||
return apply.call(fn, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
|
||||
return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
|
||||
};
|
||||
}
|
||||
else {
|
||||
/** @ignore */
|
||||
bound = function() {
|
||||
return apply.call(fn, this instanceof Dummy ? this : thisArg, arguments);
|
||||
return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
|
||||
};
|
||||
}
|
||||
Dummy.prototype = this.prototype;
|
||||
|
|
|
|||
|
|
@ -1,66 +1,66 @@
|
|||
(function() {
|
||||
|
||||
/* _ES5_COMPAT_START_ */
|
||||
if (!String.prototype.trim) {
|
||||
/* _ES5_COMPAT_START_ */
|
||||
if (!String.prototype.trim) {
|
||||
/**
|
||||
* Trims a string (removing whitespace from the beginning and the end)
|
||||
* @function external:String#trim
|
||||
* @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim">String#trim on MDN</a>
|
||||
*/
|
||||
String.prototype.trim = function () {
|
||||
// this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
|
||||
return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
|
||||
};
|
||||
}
|
||||
/* _ES5_COMPAT_END_ */
|
||||
|
||||
/**
|
||||
* Trims a string (removing whitespace from the beginning and the end)
|
||||
* @function external:String#trim
|
||||
* @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim">String#trim on MDN</a>
|
||||
* Camelizes a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to camelize
|
||||
* @return {String} Camelized version of a string
|
||||
*/
|
||||
String.prototype.trim = function () {
|
||||
// this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
|
||||
return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
|
||||
function camelize(string) {
|
||||
return string.replace(/-+(.)?/g, function(match, character) {
|
||||
return character ? character.toUpperCase() : '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to capitalize
|
||||
* @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
|
||||
* and other letters stay untouched, if false first letter is capitalized
|
||||
* and other letters are converted to lowercase.
|
||||
* @return {String} Capitalized version of a string
|
||||
*/
|
||||
function capitalize(string, firstLetterOnly) {
|
||||
return string.charAt(0).toUpperCase() +
|
||||
(firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes XML in a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to escape
|
||||
* @return {String} Escaped version of a string
|
||||
*/
|
||||
function escapeXml(string) {
|
||||
return string.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* String utilities
|
||||
* @namespace fabric.util.string
|
||||
*/
|
||||
fabric.util.string = {
|
||||
camelize: camelize,
|
||||
capitalize: capitalize,
|
||||
escapeXml: escapeXml
|
||||
};
|
||||
}
|
||||
/* _ES5_COMPAT_END_ */
|
||||
|
||||
/**
|
||||
* Camelizes a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to camelize
|
||||
* @return {String} Camelized version of a string
|
||||
*/
|
||||
function camelize(string) {
|
||||
return string.replace(/-+(.)?/g, function(match, character) {
|
||||
return character ? character.toUpperCase() : '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to capitalize
|
||||
* @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
|
||||
* and other letters stay untouched, if false first letter is capitalized
|
||||
* and other letters are converted to lowercase.
|
||||
* @return {String} Capitalized version of a string
|
||||
*/
|
||||
function capitalize(string, firstLetterOnly) {
|
||||
return string.charAt(0).toUpperCase() +
|
||||
(firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes XML in a string
|
||||
* @memberOf fabric.util.string
|
||||
* @param {String} string String to escape
|
||||
* @return {String} Escaped version of a string
|
||||
*/
|
||||
function escapeXml(string) {
|
||||
return string.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* String utilities
|
||||
* @namespace fabric.util.string
|
||||
*/
|
||||
fabric.util.string = {
|
||||
camelize: camelize,
|
||||
capitalize: capitalize,
|
||||
escapeXml: escapeXml
|
||||
};
|
||||
}());
|
||||
|
|
|
|||
|
|
@ -162,7 +162,9 @@
|
|||
* @return {Object} Object for given namespace (default fabric)
|
||||
*/
|
||||
resolveNamespace: function(namespace) {
|
||||
if (!namespace) return fabric;
|
||||
if (!namespace) {
|
||||
return fabric;
|
||||
}
|
||||
|
||||
var parts = namespace.split('.'),
|
||||
len = parts.length,
|
||||
|
|
@ -324,7 +326,7 @@
|
|||
drawDashedLine: function(ctx, x, y, x2, y2, da) {
|
||||
var dx = x2 - x,
|
||||
dy = y2 - y,
|
||||
len = sqrt(dx*dx + dy*dy),
|
||||
len = sqrt(dx * dx + dy * dy),
|
||||
rot = atan2(dy, dx),
|
||||
dc = da.length,
|
||||
di = 0,
|
||||
|
|
@ -432,22 +434,23 @@
|
|||
var a = [
|
||||
[matrixA[0], matrixA[2], matrixA[4]],
|
||||
[matrixA[1], matrixA[3], matrixA[5]],
|
||||
[0 , 0 , 1 ]
|
||||
];
|
||||
[0, 0, 1 ]
|
||||
],
|
||||
|
||||
var b = [
|
||||
b = [
|
||||
[matrixB[0], matrixB[2], matrixB[4]],
|
||||
[matrixB[1], matrixB[3], matrixB[5]],
|
||||
[0 , 0 , 1 ]
|
||||
];
|
||||
[0, 0, 1 ]
|
||||
],
|
||||
|
||||
var result = [];
|
||||
for (var r=0; r<3; r++) {
|
||||
result = [];
|
||||
|
||||
for (var r = 0; r < 3; r++) {
|
||||
result[r] = [];
|
||||
for (var c=0; c<3; c++) {
|
||||
for (var c = 0; c < 3; c++) {
|
||||
var sum = 0;
|
||||
for (var k=0; k<3; k++) {
|
||||
sum += a[r][k]*b[k][c];
|
||||
for (var k = 0; k < 3; k++) {
|
||||
sum += a[r][k] * b[k][c];
|
||||
}
|
||||
|
||||
result[r][c] = sum;
|
||||
|
|
@ -520,9 +523,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
var _isTransparent = true;
|
||||
var imageData = ctx.getImageData(
|
||||
x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
|
||||
var _isTransparent = true,
|
||||
imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
|
||||
|
||||
// Split image data - for tolerance > 1, pixelDataSize = 4;
|
||||
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
|
||||
|
|
|
|||
4
test.js
4
test.js
|
|
@ -4,9 +4,11 @@ testrunner.options.log.summary = true;
|
|||
testrunner.options.log.tests = false;
|
||||
testrunner.options.log.assertions = false;
|
||||
|
||||
testrunner.options.coverage = true;
|
||||
|
||||
testrunner.run({
|
||||
deps: "./test/fixtures/test_script.js",
|
||||
code: "./dist/all.js",
|
||||
code: "./dist/fabric.js",
|
||||
tests: [
|
||||
'./test/unit/rect.js',
|
||||
'./test/unit/ellipse.js',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* simulateEvent(@element, eventName[, options]) -> Element
|
||||
*
|
||||
*
|
||||
* - @element: element to fire event on
|
||||
* - eventName: name of event to fire (only MouseEvents and HTMLEvents interfaces are supported)
|
||||
* - options: optional object to fine-tune event properties - pointerX, pointerY, ctrlKey, etc.
|
||||
|
|
@ -29,44 +29,44 @@
|
|||
bubbles: true,
|
||||
cancelable: true
|
||||
};
|
||||
|
||||
|
||||
global.simulateEvent = function(element, eventName) {
|
||||
|
||||
|
||||
var options = extendObject(extendObject({ }, defaultOptions), arguments[2] || { }),
|
||||
oEvent,
|
||||
oEvent,
|
||||
eventType;
|
||||
|
||||
|
||||
element = typeof element == 'string' ? document.getElementById(element) : element;
|
||||
|
||||
|
||||
for (var name in eventMatchers) {
|
||||
if (eventMatchers[name].test(eventName)) {
|
||||
eventType = name;
|
||||
break;
|
||||
eventType = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!eventType) {
|
||||
throw new SyntaxError('This event is not supported');
|
||||
}
|
||||
|
||||
|
||||
if (document.createEvent) {
|
||||
try {
|
||||
// Opera doesn't support event types like "KeyboardEvent",
|
||||
// Opera doesn't support event types like "KeyboardEvent",
|
||||
// but allows to create event of type "HTMLEvents", then fire key event on it
|
||||
oEvent = document.createEvent(eventType);
|
||||
}
|
||||
catch(err) {
|
||||
oEvent = document.createEvent('HTMLEvents');
|
||||
}
|
||||
|
||||
|
||||
if (eventType == 'HTMLEvents') {
|
||||
oEvent.initEvent(eventName, options.bubbles, options.cancelable);
|
||||
}
|
||||
else if (eventType === 'KeyboardEvent') {
|
||||
// TODO (kangax): this needs to be tested
|
||||
if (oEvent.initKeyEvent) {
|
||||
oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
|
||||
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode,
|
||||
oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
|
||||
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode,
|
||||
options.charCode);
|
||||
}
|
||||
else if (oEvent.initEvent) {
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
|
||||
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
|
||||
options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
|
||||
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,18 +100,25 @@
|
|||
|
||||
test('calcOffset', function() {
|
||||
ok(typeof canvas.calcOffset == 'function', 'should respond to `calcOffset`');
|
||||
equal(canvas, canvas.calcOffset());
|
||||
equal(canvas.calcOffset(), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('add', function() {
|
||||
var rect = makeRect();
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
rect3 = makeRect(),
|
||||
rect4 = makeRect();
|
||||
|
||||
ok(typeof canvas.add == 'function');
|
||||
ok(canvas === canvas.add(rect), 'should be chainable');
|
||||
equal(canvas.item(0), rect);
|
||||
equal(canvas.add(rect1), canvas, 'should be chainable');
|
||||
strictEqual(canvas.item(0), rect1);
|
||||
|
||||
canvas.add(makeRect(), makeRect(), makeRect());
|
||||
canvas.add(rect2, rect3, rect4);
|
||||
equal(canvas.getObjects().length, 4, 'should support multiple arguments');
|
||||
|
||||
strictEqual(canvas.item(1), rect2);
|
||||
strictEqual(canvas.item(2), rect3);
|
||||
strictEqual(canvas.item(3), rect4);
|
||||
});
|
||||
|
||||
test('insertAt', function() {
|
||||
|
|
@ -124,10 +131,61 @@
|
|||
|
||||
var rect = makeRect();
|
||||
canvas.insertAt(rect, 1);
|
||||
equal(canvas.item(1), rect);
|
||||
strictEqual(canvas.item(1), rect);
|
||||
canvas.insertAt(rect, 2);
|
||||
equal(canvas.item(2), rect);
|
||||
equal(canvas, canvas.insertAt(rect, 2), 'should be chainable');
|
||||
strictEqual(canvas.item(2), rect);
|
||||
equal(canvas.insertAt(rect, 2), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('remove', function() {
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
rect3 = makeRect(),
|
||||
rect4 = makeRect();
|
||||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
ok(typeof canvas.remove == 'function');
|
||||
equal(canvas.remove(rect1), canvas, 'should be chainable');
|
||||
strictEqual(canvas.item(0), rect2, 'should be second object');
|
||||
|
||||
canvas.remove(rect2, rect3);
|
||||
strictEqual(canvas.item(0), rect4);
|
||||
|
||||
canvas.remove(rect4);
|
||||
equal(canvas.isEmpty(), true, 'canvas should be empty');
|
||||
});
|
||||
|
||||
test('before:selection:cleared', function() {
|
||||
var isFired = false;
|
||||
canvas.on('before:selection:cleared', function() { isFired = true });
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, false, 'removing inactive object shouldnt fire "before:selection:cleared"');
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.setActiveObject(canvas.item(0));
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, true, 'removing active object should fire "before:selection:cleared"');
|
||||
});
|
||||
|
||||
test('selection:cleared', function() {
|
||||
var isFired = false;
|
||||
canvas.on('selection:cleared', function() { isFired = true });
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, false, 'removing inactive object shouldnt fire "selection:cleared"');
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.setActiveObject(canvas.item(0));
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, true, 'removing active object should fire "selection:cleared"');
|
||||
});
|
||||
|
||||
test('getContext', function() {
|
||||
|
|
@ -136,13 +194,13 @@
|
|||
|
||||
test('clearContext', function() {
|
||||
ok(typeof canvas.clearContext == 'function');
|
||||
equal(canvas, canvas.clearContext(canvas.getContext()), 'chainable');
|
||||
equal(canvas.clearContext(canvas.getContext()), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('clear', function() {
|
||||
ok(typeof canvas.clear == 'function');
|
||||
|
||||
equal(canvas, canvas.clear());
|
||||
equal(canvas.clear(), canvas, 'should be chainable');
|
||||
equal(canvas.getObjects().length, 0);
|
||||
});
|
||||
|
||||
|
|
@ -419,6 +477,25 @@
|
|||
});
|
||||
});
|
||||
|
||||
asyncTest('loadFromJSON without "objects" property', function() {
|
||||
var c1 = new fabric.Canvas('c1', { backgroundColor: 'green', overlayColor: 'yellow' }),
|
||||
c2 = new fabric.Canvas('c2', { backgroundColor: 'red', overlayColor: 'orange' });
|
||||
|
||||
var json = c1.toJSON();
|
||||
var fired = false;
|
||||
|
||||
delete json.objects;
|
||||
|
||||
c2.loadFromJSON(json, function() {
|
||||
fired = true;
|
||||
|
||||
ok(fired, 'Callback should be fired even if no "objects" property exists');
|
||||
equal(c2.backgroundColor, 'green', 'Color should be set properly');
|
||||
equal(c2.overlayColor, 'yellow', 'Color should be set properly');
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest('loadFromJSON with empty fabric.Group', function() {
|
||||
var c1 = new fabric.Canvas('c1'),
|
||||
c2 = new fabric.Canvas('c2'),
|
||||
|
|
@ -501,14 +578,6 @@
|
|||
// }, 1000);
|
||||
// });
|
||||
|
||||
test('remove', function() {
|
||||
ok(typeof canvas.remove == 'function');
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect();
|
||||
canvas.add(rect1, rect2);
|
||||
equal(canvas.remove(rect1), rect1, 'should return removed object');
|
||||
equal(canvas.item(0), rect2, 'only second object should be left');
|
||||
});
|
||||
|
||||
test('sendToBack', function() {
|
||||
ok(typeof canvas.sendToBack == 'function');
|
||||
|
|
@ -680,7 +749,7 @@
|
|||
makeRect({ left: 20, top: 20 })
|
||||
]);
|
||||
|
||||
equal(canvas.setActiveGroup(group), canvas, 'chainable');
|
||||
equal(canvas.setActiveGroup(group), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveGroup(), group);
|
||||
});
|
||||
|
||||
|
|
@ -704,7 +773,7 @@
|
|||
ok(typeof canvas.discardActiveGroup == 'function');
|
||||
var group = new fabric.Group([makeRect(), makeRect()]);
|
||||
canvas.setActiveGroup(group);
|
||||
equal(canvas.discardActiveGroup(), canvas, 'chainable');
|
||||
equal(canvas.discardActiveGroup(), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveGroup(), null, 'removing active group sets it to null');
|
||||
});
|
||||
|
||||
|
|
@ -867,14 +936,14 @@
|
|||
ok(typeof canvas.getWidth == 'function');
|
||||
|
||||
equal(canvas.getWidth(), 600);
|
||||
equal(canvas.setWidth(444), canvas, 'chainable');
|
||||
equal(canvas.setWidth(444), canvas, 'should be chainable');
|
||||
equal(canvas.getWidth(), 444);
|
||||
});
|
||||
|
||||
test('getSetHeight', function() {
|
||||
ok(typeof canvas.getHeight == 'function');
|
||||
equal(canvas.getHeight(), 600);
|
||||
equal(canvas.setHeight(765), canvas, 'chainable');
|
||||
equal(canvas.setHeight(765), canvas, 'should be chainable');
|
||||
equal(canvas.getHeight(), 765);
|
||||
});
|
||||
|
||||
|
|
@ -906,25 +975,6 @@
|
|||
ok(canvas.containsPoint(eventStub, rect), 'on rect at (200, 200) should be within area (175, 175, 225, 225)');
|
||||
});
|
||||
|
||||
// asyncTest('resizeImageToFit', function() {
|
||||
// ok(typeof canvas._resizeImageToFit == 'function');
|
||||
|
||||
// var imgEl = fabric.util.makeElement('img', { src: '../fixtures/very_large_image.jpg' }),
|
||||
// ORIGINAL_WIDTH = 3888,
|
||||
// ORIGINAL_HEIGHT = 2592;
|
||||
|
||||
// setTimeout(function() {
|
||||
// equal(imgEl.width, ORIGINAL_WIDTH);
|
||||
// equal(imgEl.height, ORIGINAL_HEIGHT);
|
||||
|
||||
// canvas._resizeImageToFit(imgEl);
|
||||
|
||||
// ok(imgEl.width < ORIGINAL_WIDTH);
|
||||
|
||||
// start();
|
||||
// }, 2000);
|
||||
// });
|
||||
|
||||
asyncTest('fxRemove', function() {
|
||||
ok(typeof canvas.fxRemove == 'function');
|
||||
|
||||
|
|
@ -973,22 +1023,6 @@
|
|||
// }, 1000);
|
||||
// });
|
||||
|
||||
test('selection:cleared', function() {
|
||||
var isFired = false;
|
||||
canvas.on('selection:cleared', function() { isFired = true });
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, false, 'removing inactive object shouldnt fire "selection:cleared"');
|
||||
|
||||
canvas.add(new fabric.Rect());
|
||||
canvas.setActiveObject(canvas.item(0));
|
||||
canvas.remove(canvas.item(0));
|
||||
|
||||
equal(isFired, true, 'removing active object should fire "selection:cleared"');
|
||||
});
|
||||
|
||||
test('clipTo', function() {
|
||||
canvas.clipTo = function(ctx) {
|
||||
ctx.arc(0, 0, 10, 0, Math.PI * 2, false);
|
||||
|
|
|
|||
|
|
@ -190,18 +190,25 @@
|
|||
|
||||
test('calcOffset', function() {
|
||||
ok(typeof canvas.calcOffset == 'function', 'should respond to `calcOffset`');
|
||||
equal(canvas, canvas.calcOffset());
|
||||
equal(canvas.calcOffset(), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('add', function() {
|
||||
var rect = makeRect();
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
rect3 = makeRect(),
|
||||
rect4 = makeRect();
|
||||
|
||||
ok(typeof canvas.add == 'function');
|
||||
ok(canvas === canvas.add(rect), 'should be chainable');
|
||||
equal(canvas.item(0), rect);
|
||||
equal(canvas.add(rect1), canvas, 'should be chainable');
|
||||
strictEqual(canvas.item(0), rect1);
|
||||
|
||||
canvas.add(makeRect(), makeRect(), makeRect());
|
||||
canvas.add(rect2, rect3, rect4);
|
||||
equal(canvas.getObjects().length, 4, 'should support multiple arguments');
|
||||
|
||||
strictEqual(canvas.item(1), rect2);
|
||||
strictEqual(canvas.item(2), rect3);
|
||||
strictEqual(canvas.item(3), rect4);
|
||||
});
|
||||
|
||||
test('add renderOnAddRemove disabled', function() {
|
||||
|
|
@ -218,7 +225,7 @@
|
|||
|
||||
canvas.on('after:render', countRenderAll);
|
||||
|
||||
ok(canvas === canvas.add(rect), 'should be chainable');
|
||||
equal(canvas.add(rect), canvas, 'should be chainable');
|
||||
equal(renderAllCount, 0);
|
||||
|
||||
equal(canvas.item(0), rect);
|
||||
|
|
@ -234,6 +241,32 @@
|
|||
canvas.renderOnAddRemove = originalRenderOnAddition;
|
||||
});
|
||||
|
||||
test('object:added', function() {
|
||||
var objectsAdded = [];
|
||||
|
||||
canvas.on('object:added', function(e) {
|
||||
objectsAdded.push(e.target);
|
||||
});
|
||||
|
||||
var rect = new fabric.Rect({ width: 10, height: 20 });
|
||||
canvas.add(rect);
|
||||
|
||||
deepEqual(objectsAdded[0], rect);
|
||||
|
||||
var circle1 = new fabric.Circle(),
|
||||
circle2 = new fabric.Circle();
|
||||
|
||||
canvas.add(circle1, circle2);
|
||||
|
||||
strictEqual(objectsAdded[1], circle1);
|
||||
strictEqual(objectsAdded[2], circle2);
|
||||
|
||||
var circle3 = new fabric.Circle();
|
||||
canvas.insertAt(circle3, 2);
|
||||
|
||||
strictEqual(objectsAdded[3], circle3);
|
||||
});
|
||||
|
||||
test('insertAt', function() {
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect();
|
||||
|
|
@ -244,10 +277,10 @@
|
|||
|
||||
var rect = makeRect();
|
||||
canvas.insertAt(rect, 1);
|
||||
equal(canvas.item(1), rect);
|
||||
strictEqual(canvas.item(1), rect);
|
||||
canvas.insertAt(rect, 2);
|
||||
equal(canvas.item(2), rect);
|
||||
equal(canvas, canvas.insertAt(rect, 2), 'should be chainable');
|
||||
strictEqual(canvas.item(2), rect);
|
||||
equal(canvas.insertAt(rect, 2), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('insertAt renderOnAddRemove disabled', function() {
|
||||
|
|
@ -273,7 +306,7 @@
|
|||
canvas.insertAt(rect, 1);
|
||||
equal(renderAllCount, 0);
|
||||
|
||||
equal(canvas.item(1), rect);
|
||||
strictEqual(canvas.item(1), rect);
|
||||
canvas.insertAt(rect, 2);
|
||||
equal(renderAllCount, 0);
|
||||
|
||||
|
|
@ -284,15 +317,90 @@
|
|||
canvas.renderOnAddRemove = originalRenderOnAddition;
|
||||
});
|
||||
|
||||
test('remove', function() {
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
rect3 = makeRect(),
|
||||
rect4 = makeRect();
|
||||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
ok(typeof canvas.remove == 'function');
|
||||
equal(canvas.remove(rect1), canvas, 'should be chainable');
|
||||
strictEqual(canvas.item(0), rect2, 'should be second object');
|
||||
|
||||
canvas.remove(rect2, rect3);
|
||||
strictEqual(canvas.item(0), rect4);
|
||||
|
||||
canvas.remove(rect4);
|
||||
equal(canvas.isEmpty(), true, 'canvas should be empty');
|
||||
});
|
||||
|
||||
test('remove renderOnAddRemove disabled', function() {
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
originalRenderOnAddition,
|
||||
renderAllCount = 0;
|
||||
|
||||
function countRenderAll() {
|
||||
renderAllCount++;
|
||||
}
|
||||
|
||||
originalRenderOnAddition = canvas.renderOnAddRemove;
|
||||
canvas.renderOnAddRemove = false;
|
||||
|
||||
canvas.on('after:render', countRenderAll);
|
||||
|
||||
canvas.add(rect1, rect2);
|
||||
equal(renderAllCount, 0);
|
||||
|
||||
equal(canvas.remove(rect1), canvas, 'should be chainable');
|
||||
equal(renderAllCount, 0);
|
||||
strictEqual(canvas.item(0), rect2, 'only second object should be left');
|
||||
|
||||
canvas.renderAll();
|
||||
equal(renderAllCount, 1);
|
||||
|
||||
canvas.off('after:render', countRenderAll);
|
||||
canvas.renderOnAddRemove = originalRenderOnAddition;
|
||||
});
|
||||
|
||||
test('object:removed', function() {
|
||||
var objectsRemoved = [];
|
||||
|
||||
canvas.on('object:removed', function(e) {
|
||||
objectsRemoved.push(e.target);
|
||||
});
|
||||
|
||||
var rect = new fabric.Rect({ width: 10, height: 20 }),
|
||||
circle1 = new fabric.Circle(),
|
||||
circle2 = new fabric.Circle();
|
||||
|
||||
canvas.add(rect, circle1, circle2);
|
||||
|
||||
strictEqual(canvas.item(0), rect);
|
||||
strictEqual(canvas.item(1), circle1);
|
||||
strictEqual(canvas.item(2), circle2);
|
||||
|
||||
canvas.remove(rect);
|
||||
strictEqual(objectsRemoved[0], rect);
|
||||
|
||||
canvas.remove(circle1, circle2);
|
||||
strictEqual(objectsRemoved[1], circle1);
|
||||
strictEqual(objectsRemoved[2], circle2);
|
||||
|
||||
equal(canvas.isEmpty(), true, 'canvas should be empty');
|
||||
});
|
||||
|
||||
test('clearContext', function() {
|
||||
ok(typeof canvas.clearContext == 'function');
|
||||
equal(canvas, canvas.clearContext(canvas.contextContainer), 'chainable');
|
||||
equal(canvas.clearContext(canvas.contextContainer), canvas, 'should be chainable');
|
||||
});
|
||||
|
||||
test('clear', function() {
|
||||
ok(typeof canvas.clear == 'function');
|
||||
|
||||
equal(canvas, canvas.clear());
|
||||
equal(canvas.clear(), canvas, 'should be chainable');
|
||||
equal(canvas.getObjects().length, 0);
|
||||
});
|
||||
|
||||
|
|
@ -665,44 +773,6 @@
|
|||
});
|
||||
});
|
||||
|
||||
test('remove', function() {
|
||||
ok(typeof canvas.remove == 'function');
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect();
|
||||
canvas.add(rect1, rect2);
|
||||
equal(canvas.remove(rect1), rect1, 'should return removed object');
|
||||
equal(canvas.item(0), rect2, 'only second object should be left');
|
||||
});
|
||||
|
||||
test('remove renderOnAddRemove disabled', function() {
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
originalRenderOnAddition,
|
||||
renderAllCount = 0;
|
||||
|
||||
function countRenderAll() {
|
||||
renderAllCount++;
|
||||
}
|
||||
|
||||
originalRenderOnAddition = canvas.renderOnAddRemove;
|
||||
canvas.renderOnAddRemove = false;
|
||||
|
||||
canvas.on('after:render', countRenderAll);
|
||||
|
||||
canvas.add(rect1, rect2);
|
||||
equal(renderAllCount, 0);
|
||||
|
||||
equal(canvas.remove(rect1), rect1, 'should return removed object');
|
||||
equal(renderAllCount, 0);
|
||||
equal(canvas.item(0), rect2, 'only second object should be left');
|
||||
|
||||
canvas.renderAll();
|
||||
equal(renderAllCount, 1);
|
||||
|
||||
canvas.off('after:render', countRenderAll);
|
||||
canvas.renderOnAddRemove = originalRenderOnAddition;
|
||||
});
|
||||
|
||||
test('sendToBack', function() {
|
||||
ok(typeof canvas.sendToBack == 'function');
|
||||
|
||||
|
|
@ -940,36 +1010,17 @@
|
|||
test('getSetWidth', function() {
|
||||
ok(typeof canvas.getWidth == 'function');
|
||||
equal(canvas.getWidth(), 600);
|
||||
equal(canvas.setWidth(444), canvas, 'chainable');
|
||||
equal(canvas.setWidth(444), canvas, 'should be chainable');
|
||||
equal(canvas.getWidth(), 444);
|
||||
});
|
||||
|
||||
test('getSetHeight', function() {
|
||||
ok(typeof canvas.getHeight == 'function');
|
||||
equal(canvas.getHeight(), 600);
|
||||
equal(canvas.setHeight(765), canvas, 'chainable');
|
||||
equal(canvas.setHeight(765), canvas, 'should be chainable');
|
||||
equal(canvas.getHeight(), 765);
|
||||
});
|
||||
|
||||
// asyncTest('resizeImageToFit', function() {
|
||||
// ok(typeof canvas._resizeImageToFit == 'function');
|
||||
|
||||
// var imgEl = fabric.util.makeElement('img', { src: '../fixtures/very_large_image.jpg' }),
|
||||
// ORIGINAL_WIDTH = 3888,
|
||||
// ORIGINAL_HEIGHT = 2592;
|
||||
|
||||
// setTimeout(function() {
|
||||
// equal(imgEl.width, ORIGINAL_WIDTH);
|
||||
// equal(imgEl.height, ORIGINAL_HEIGHT);
|
||||
|
||||
// canvas._resizeImageToFit(imgEl);
|
||||
|
||||
// ok(imgEl.width < ORIGINAL_WIDTH);
|
||||
|
||||
// start();
|
||||
// }, 2000);
|
||||
// });
|
||||
|
||||
asyncTest('fxRemove', function() {
|
||||
ok(typeof canvas.fxRemove == 'function');
|
||||
|
||||
|
|
@ -982,7 +1033,7 @@
|
|||
}
|
||||
|
||||
ok(canvas.item(0) === rect);
|
||||
ok(canvas.fxRemove(rect, { onComplete: onComplete }) === canvas, 'should be chainable');
|
||||
equal(canvas.fxRemove(rect, { onComplete: onComplete }), canvas, 'should be chainable');
|
||||
|
||||
setTimeout(function() {
|
||||
equal(canvas.item(0), undefined);
|
||||
|
|
@ -1017,30 +1068,4 @@
|
|||
// }, 1000);
|
||||
// });
|
||||
|
||||
test('object:added', function() {
|
||||
|
||||
var objectsAdded = [];
|
||||
canvas.on('object:added', function(e) {
|
||||
objectsAdded.push(e.target);
|
||||
});
|
||||
|
||||
var rect = new fabric.Rect({ width: 10, height: 20 });
|
||||
canvas.add(rect);
|
||||
|
||||
deepEqual(objectsAdded[0], rect);
|
||||
|
||||
var circle1 = new fabric.Circle(),
|
||||
circle2 = new fabric.Circle();
|
||||
|
||||
canvas.add(circle1, circle2);
|
||||
|
||||
deepEqual(objectsAdded[1], circle1);
|
||||
deepEqual(objectsAdded[2], circle2);
|
||||
|
||||
var circle3 = new fabric.Circle();
|
||||
canvas.insertAt(circle3, 2);
|
||||
|
||||
deepEqual(objectsAdded[3], circle3);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue