Skip to content

Instantly share code, notes, and snippets.

@clouddueling
Last active June 15, 2021 18:33
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save clouddueling/8475865 to your computer and use it in GitHub Desktop.
Save clouddueling/8475865 to your computer and use it in GitHub Desktop.
common.fabric module for angular/common
/**
* http://fabricjs.com/js/kitchensink/controller.js
* http://fabricjs.com/js/kitchensink/utils.js
* http://fabricjs.com/js/kitchensink/app_config.js
* http://fabricjs.com/events/
* view-source:http://fabricjs.com/kitchensink/
*/
(function() {
'use strict';
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
angular.module('common.fabric', [])
.factory('FabricService', [function() {
var multiplier = 2;
var self = {};
function update() {
if (!self.canvas) {
return;
}
self.canvas.fire('canvas:modified');
self.canvas.renderAll();
}
function getActiveStyle(styleName, object) {
if (!self.canvas) {
return;
}
object = object || self.canvas.getActiveObject();
if (!object) {
return;
}
return (object.getSelectionStyles && object.isEditing)
? (object.getSelectionStyles()[styleName] || '')
: (object[styleName] || '');
}
function setActiveStyle(styleName, value, object) {
if (!self.canvas) {
return;
}
object = object || self.canvas.getActiveObject();
if (!object) {
return;
}
if (object.setSelectionStyles && object.isEditing) {
var style = { };
style[styleName] = value;
object.setSelectionStyles(style);
object.setCoords();
}
else {
object[styleName] = value;
}
object.setCoords();
update();
self.canvas.renderAll();
}
function getActiveProp(name) {
if (!self.canvas) {
return;
}
var object = self.canvas.getActiveObject();
if (!object) {
return;
}
return object[name] || '';
}
function setActiveProp(name, value) {
var object = self.canvas.getActiveObject();
if (!object) {
return;
}
object.set(name, value).setCoords();
update();
self.canvas.renderAll();
}
self.canvas = undefined;
self.rotatingPointOffset = 20;
self.element = {};
self.canvasId = 'fabric-' + Math.floor(Math.random() * 10000000);
self.init = function(json) {
self.element.attr('id', self.canvasId);
self.canvas = new fabric.Canvas(self.canvasId);
};
self.loadJson = function(json) {
self.canvas.loadFromJSON(json, self.canvas.renderAll(self.canvas));
};
self.addShape = function(shapePath) {
fabric.loadSVGFromURL(shapePath, function(objects, options) {
var object = fabric.util.groupSVGElements(objects, options);
object.top = 20;
object.left = 20;
object.angle = 0;
object.opacity = 100;
object.rotatingPointOffset = self.rotatingPointOffset;
object.padding = 5;
object.borderColor = 'EEF6FC';
object.cornerColor = 'rgba(64, 159, 221, .3)';
object.cornerSize = 8;
object.transparentCorners = false;
if (object.isSameColor && object.isSameColor() || !object.paths) {
object.setFill('#0088cc');
} else if (object.paths) {
for (var i = 0; i < object.paths.length; i++) {
object.paths[i].setFill('#0088cc');
}
}
self.canvas.add(object);
object.active = true;
object.bringToFront();
});
};
self.addImage = function(image) {
fabric.Image.fromURL(image, function (object) {
object.top = 20;
object.left = 20;
object.rotatingPointOffset = self.rotatingPointOffset;
object.padding = 5;
object.borderColor = 'EEF6FC';
object.cornerColor = 'rgba(64, 159, 221, .3)';
object.cornerSize = 8;
object.transparentCorners = false;
self.canvas.add(object);
object.active = true;
object.bringToFront();
});
};
self.addText = function(str) {
str = str || "New Text";
var text = new fabric.Text(str, {
left: 20,
top: 20,
color: '#454545',
fontFamily: 'Open Sans',
fontWeight: '',
textDecoration: '',
fontStyle: '',
textAlign: 'center',
fontSize: '40',
rotatingPointOffset: self.rotatingPointOffset,
angle: 0,
padding: 5,
borderColor: 'EEF6FC',
cornerColor: 'rgba(64, 159, 221, .3)',
cornerSize: 8,
transparentCorners: false,
hasRotatingPoint: true,
centerTransform: true
});
self.canvas.add(text);
text.active = true;
text.bringToFront();
};
self.setBackgroundColor = function(color) {
self.canvas.setBackgroundColor(color);
update();
};
self.setWidth = function(width) {
self.canvas.setWidth(width);
update();
};
self.setHeight = function(height) {
self.canvas.setHeight(height);
update();
};
self.clearCanvas = function() {
self.canvas.clear();
self.canvas.setBackgroundColor('ffffff');
update();
};
self.deselectActiveObject = function() {
var object = self.canvas.getActiveObject();
if (object) {
object.active = false;
}
};
self.deactivateAll = function() {
self.canvas.deactivateAll().renderAll();
};
self.getCanvasData = function() {
var data = self.canvas.toDataURL({
width: self.canvas.getWidth(),
height: self.canvas.getHeight(),
multiplier: multiplier
});
return data;
};
self.download = function() {
// If there is a focused object deselect it and after the download is initialized reselct it.
var object = self.canvas.getActiveObject();
self.deselectActiveObject();
// If zoom is less than 1 a small version is created with a lot of white space.
var data = self.getCanvasData().replace("image/png", "image/octet-stream");
var filename = 'Untitled Image.png';
var link = document.createElement('a');
link.download = filename;
link.href = data;
link.click();
if (object) {
object.active = true;
}
};
self.removeSelected = function() {
var activeObject = self.canvas.getActiveObject(),
activeGroup = self.canvas.getActiveGroup();
if (activeGroup) {
var objectsInGroup = activeGroup.getObjects();
self.canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
self.canvas.remove(object);
});
} else if (activeObject) {
self.canvas.remove(activeObject);
}
};
self.getType = function() {
return getActiveProp('type');
};
self.getOpacity = function() {
return getActiveStyle('opacity') * 100;
};
self.setOpacity = function(value) {
setActiveStyle('opacity', parseInt(value, 10) / 100);
};
self.getFill = function() {
return getActiveStyle('fill');
};
self.setFill = function(value) {
if (! self.canvas) {
return;
}
// Yes this looks like shit but it works for text and uploaded shapes who both use the same prop.
var object = self.canvas.getActiveObject();
if (object) {
if (object.type === 'text') {
setActiveStyle('fill', value);
} else {
if (object.isSameColor && object.isSameColor() || !object.paths) {
object.setFill(value);
} else if (object.paths) {
for (var i = 0; i < object.paths.length; i++) {
object.paths[i].setFill(value);
}
}
self.canvas.renderAll();
}
}
};
self.isBold = function() {
return getActiveStyle('fontWeight') === 'bold';
};
self.toggleBold = function() {
setActiveStyle('fontWeight',
getActiveStyle('fontWeight') === 'bold' ? '' : 'bold');
};
self.isItalic = function() {
return getActiveStyle('fontStyle') === 'italic';
};
self.toggleItalic = function() {
setActiveStyle('fontStyle',
getActiveStyle('fontStyle') === 'italic' ? '' : 'italic');
};
self.isUnderline = function() {
return getActiveStyle('textDecoration').indexOf('underline') > -1;
};
self.toggleUnderline = function() {
var value = self.isUnderline()
? getActiveStyle('textDecoration').replace('underline', '')
: (getActiveStyle('textDecoration') + ' underline');
setActiveStyle('textDecoration', value);
};
self.isLinethrough = function() {
return getActiveStyle('textDecoration').indexOf('line-through') > -1;
};
self.toggleLinethrough = function() {
var value = self.isLinethrough()
? getActiveStyle('textDecoration').replace('line-through', '')
: (getActiveStyle('textDecoration') + ' line-through');
setActiveStyle('textDecoration', value);
};
self.isOverline = function() {
return getActiveStyle('textDecoration').indexOf('overline') > -1;
};
self.toggleOverline = function() {
var value = self.isOverline()
? getActiveStyle('textDecoration').replace('overline', '')
: (getActiveStyle('textDecoration') + ' overline');
setActiveStyle('textDecoration', value);
};
self.getText = function() {
return getActiveProp('text');
};
self.setText = function(value) {
setActiveProp('text', value);
};
self.getTextAlign = function() {
return capitalize(getActiveProp('textAlign'));
};
self.setTextAlign = function(value) {
setActiveProp('textAlign', value.toLowerCase());
};
self.getFontFamilygetStrokeColor = function() {
return getActiveProp('fontFamily').toLowerCase();
};
self.setFontFamily = function(value) {
setActiveProp('fontFamily', value.toLowerCase());
};
self.getBgColor = function() {
return getActiveProp('backgroundColor');
};
self.setBgColor = function(value) {
setActiveProp('backgroundColor', value);
};
self.getFlipX = function() {
return getActiveProp('flipX');
};
self.setFlipX = function(value) {
setActiveProp('flipX', value);
};
self.toggleFlipX = function() {
var value = self.getFlipX() ? false : true;
self.setFlipX(value);
};
self.getTextBgColor = function() {
return getActiveProp('textBackgroundColor');
};
self.setTextBgColor = function(value) {
setActiveProp('textBackgroundColor', value);
};
self.getStrokeColor = function() {
return getActiveStyle('stroke');
};
self.setStrokeColor = function(value) {
setActiveStyle('stroke', value);
};
self.getStrokeWidth = function() {
return getActiveStyle('strokeWidth');
};
self.setStrokeWidth = function(value) {
setActiveStyle('strokeWidth', parseInt(value, 10));
};
self.getFontSize = function() {
return getActiveStyle('fontSize');
};
self.setFontSize = function(value) {
setActiveStyle('fontSize', parseInt(value, 10));
};
self.getLineHeight = function() {
return getActiveStyle('lineHeight');
};
self.setLineHeight = function(value) {
setActiveStyle('lineHeight', parseFloat(value, 10));
};
self.getBold = function() {
return getActiveStyle('fontWeight');
};
self.setBold = function(value) {
setActiveStyle('fontWeight', value ? 'bold' : '');
};
self.getCanvasBgColor = function() {
return self.canvas.backgroundColor;
};
self.setCanvasBgColor = function(value) {
self.canvas.backgroundColor = value;
self.canvas.renderAll();
};
self.sendBackwards = function() {
var activeObject = self.canvas.getActiveObject();
if (activeObject) {
self.canvas.sendBackwards(activeObject);
}
};
self.sendToBack = function() {
var activeObject = self.canvas.getActiveObject();
if (activeObject) {
self.canvas.sendToBack(activeObject);
}
};
self.bringForward = function() {
var activeObject = self.canvas.getActiveObject();
if (activeObject) {
self.canvas.bringForward(activeObject);
}
};
self.bringToFront = function() {
var activeObject = self.canvas.getActiveObject();
if (activeObject) {
self.canvas.bringToFront(activeObject);
}
};
self.loadJson = function(json) {
self.canvas.loadFromJSON(json);
var obj = angular.fromJson(json);
self.setWidth(obj.width);
self.setHeight(obj.height);
};
return self;
}])
.directive('fabric', ['FabricService', '$timeout', function(FabricService, $timeout) {
return {
scope: {
ngModel: '=',
ngChange: '&'
},
link: function(scope, element) {
fabric.Object.prototype.transparentCorners = false;
function update() {
$timeout(function() {
scope.ngModel = FabricService.canvas.toJSON(['height', 'width', 'backgroundColor']);
scope.ngChange();
});
}
function initCanvas() {
FabricService.element = element;
FabricService.init(scope.ngModel);
}
function initListeners() {
FabricService.canvas.on('canvas:modified', function(options) {
update();
});
FabricService.canvas.on('object:modified', function(options) {
FabricService.canvas.renderAll();
update();
});
FabricService.canvas.on("object:selected", function(){
$timeout(function() {
update();
});
});
FabricService.canvas.on("selection:cleared", function(){
$timeout(function() {
scope.fabricActiveObject = undefined;
});
});
FabricService.canvas.on("after:render", function(){
FabricService.canvas.calcOffset();
});
}
initCanvas();
initListeners();
}
};
}])
.directive('fabricValue', ['FabricService', function(FabricService) {
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
var prop = capitalize($attrs.fabricValue),
getter = 'get' + prop,
setter = 'set' + prop;
$element.on('change keyup select', function() {
FabricService[setter] && FabricService[setter](this.value);
FabricService.canvas.fire('canvas:modified');
});
$scope.$watch(FabricService[getter], function(newVal) {
if (!FabricService.canvas) {
return;
}
if ($element[0].type === 'radio') {
var radioGroup = document.getElementsByName($element[0].name);
for (var i = 0, len = radioGroup.length; i < len; i++) {
radioGroup[i].checked = radioGroup[i].value === newVal;
}
} else {
$element.val(newVal);
}
FabricService.canvas.fire('canvas:modified');
});
}
};
}]);
})();
(function() {
'use strict';
angular.module('builders.images', [])
.factory('ImagesService', [function() {
var self = {};
self.exampleSizes = [
{
name: 'Paper (8.5 x 11)',
height: 1100,
width: 850
},
{
name: 'Paper (11 x 8.5)',
height: 850,
width: 1100
},
{
name: 'Business Card (3.5 x 2)',
height: 440,
width: 770
},
{
name: 'Postcard (6 x 4)',
height: 570,
width: 855
},
{
name: 'Content/Builder Product Thumbnail',
height: 400,
width: 760
},
{
name: 'Badge',
height: 400,
width: 400
}
];
self.fonts = [
{ name: 'Sonsie One' },
{ name: 'Croissant One' },
{ name: 'Graduate' },
{ name: 'Open Sans' },
{ name: 'Krona One' },
{ name: 'Mouse Memoirs' },
{ name: 'Stoke' },
{ name: 'Indie Flower' },
{ name: 'Ribeye Marrow' },
{ name: 'Courgette' },
{ name: 'Gruppo' },
{ name: 'Ranchers' }
];
self.imageHandle = {
step: 'previous'
};
self.shapeCategories = [
{
name: "Popular Shapes",
shapes: [
'arrow6',
'bubble3',
'checkmark1',
'circle1',
'rectangle1',
'star1',
'triangle1',
'arrow7'
]
},
{
name: "Simple Shapes",
shapes: [
'circle1',
'heart1',
'rectangle1',
'triangle1',
'star1',
'star2',
'star3',
'star4'
]
},
{
name: "Arrows & Pointers",
shapes: [
'arrow1',
'arrow2',
'arrow3',
'arrow4',
'arrow6',
'arrow7',
'arrow8'
]
},
{
name: "Bubbles & Balloons",
shapes: [
'bubble2',
'bubble3'
]
},
{
name: "Check Marks",
shapes: [
'checkmark1'
]
}
];
return self;
}])
.controller('ImagesCtrl', ['$scope', 'ImagesService', 'FabricService', 'Page', 'Modal', function($scope, ImagesService, FabricService, Page, Modal) {
var handle;
$scope.ImagesService = ImagesService;
$scope.FabricService = FabricService;
$scope.updatePage = function(page) {
$scope.$timeout.cancel(handle);
handle = $scope.$timeout(function() {
Page.update(page.id, {
name: page.name,
json: page.json
});
}, 500);
};
$scope.loadPage = function(pageId) {
if (!pageId) {
return;
}
Page.load(pageId).success(function(data) {
$scope.page = data.page;
$scope.pageId = data.page.id;
var obj = angular.fromJson(data.page.json);
$scope.page.width = obj.width;
$scope.page.height = obj.height;
FabricService.loadJson(data.page.json);
});
};
$scope.createPage = function(cardId) {
Page.create({
card_id: cardId
}).success(function(data) {
$scope.card.pages.push(data.page);
$scope.loadPage(data.page.id);
});
};
$scope.deletePage = function(pageId) {
Page.delete(pageId).success(function() {
$scope.card.pages = _.reject($scope.card.pages, function(page) {
return page.id === pageId;
});
$scope.loadFirstPage();
});
};
$scope.loadFirstPage = function() {
if ($scope.card.pages.length === 0) {
return;
}
$scope.pageId = $scope.card.pages[0].id;
$scope.loadPage($scope.pageId);
};
$scope.updatePageOrder = function() {
var pageIds = _.pluck($scope.card.pages, 'id');
$scope.Card.updatePageOrder($scope.card.id, {
page_ids: pageIds
});
};
$scope.addUploadedImage = function(requestText) {
var obj = angular.fromJson(requestText);
FabricService.addImage('/image?image=' + obj.filename + '&size=2000');
Modal.close();
};
$scope.addShape = function(shape) {
FabricService.addShape('/lib/svg/' + shape + '.svg');
Modal.close();
};
$scope.loadUploads = function() {
};
ImagesService.selectedShapeCategory = ImagesService.shapeCategories[0];
$scope.loadFirstPage();
}]);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment