Skip to content

Instantly share code, notes, and snippets.

Created March 26, 2015 18:43
Show Gist options
  • Save anonymous/ebf51cccaf86402add2b to your computer and use it in GitHub Desktop.
Save anonymous/ebf51cccaf86402add2b to your computer and use it in GitHub Desktop.
AngularJS and FabricJS

AngularJS and FabricJS

An example of binding AngularJS to a FabricJS canvas by using a service as a way of mimicking $scope for FabricJS.

A Pen by Michael Calkins on CodePen.

License.

<a class='btn btn-info' target='_blank' href='https://github.com/clouddueling/angular-fabric'>
<i class='fa fa-github'></i>
clouddueling/angular-fabric
</a>
<div ng-app='example' ng-controller="ExampleCtrl">
<div class='image-builder-container'>
<div ng-if="fabric.isLoading()" class="image-loading">
<div class="loading-indicator"></div>
</div>
<div class="row">
<div class="col-sm-12">
<small><em class='text-muted'>Image and shape do not work due to codepen cross-origin.</em></small>
<br />
<div class='btn-group pull-left mrm'>
<button ng-disabled="!fabric.selectedObject" ng-click="fabric.deleteActiveObject(); fabric.setDirty(true)" class='btn btn-danger'>
<i class='fa fa-trash-o'></i>
<div class='clearfix'></div>
Delete
</button>
<button ng-click='addImage()' class='btn btn-default'>
<i class='fa fa-image'></i>
<div class='clearfix'></div>
Image
</button>
<button ng-click="fabric.addText()" class='btn btn-default'>
<i class='fa fa-font'></i>
<div class='clearfix'></div>
&nbsp; Text &nbsp;
</button>
<button ng-click='addShape()' class='btn btn-default'>
<i class='fa fa-star'></i>
<div class='clearfix'></div>
Shapes
</button>
<button ng-click="fabric.clearCanvas(); fabric.setDirty(true)" class='btn btn-default'>
<i class='fa fa-refresh'></i>
<div class='clearfix'></div>
Restart
</button>
</div>
<div class='pull-left' style='margin: 15px 0 0 10px;'>
<div class='row'>
<div class='col-xs-1 text-center'>
<i class='fa fa-search-minus pull-left'></i>
</div>
<div class='col-xs-6'>
<input type='range' class='pull-left' min='.1' max='3' step='.1' ng-change='fabric.setZoom()' ng-model='fabric.canvasScale' />
</div>
<div class='col-xs-1 text-center'>
<i class='fa fa-search-plus'></i>
</div>
<div class='col-xs-3'>
<button class='btn btn-xs' ng-click="fabric.resetZoom()">
Reset
</button>
</div>
</div>
</div>
<div class="pull-right">
<button class='btn btn-default' title='Dummy button. This is where you would take the json from the canvas and save it to your database.'>
<i class='fa fa-save'></i>
<div class='clearfix'></div>
Save <span ng-show='fabric.isDirty()' class='text-danger'>*</span>
</button>
<button ng-click="fabric.download('myCanvas')" class='btn btn-success'>
<i class='fa fa-download'></i>
<div class='clearfix'></div>
Download
</button>
</div>
</div>
</div>
<br />
<div class="row">
<div class="col-xs-4">
<label>Background Color</label>
<input type="text" class='form-control' ng-change='fabric.setDirty(true); fabric.stopContinuousRendering()' ng-model="fabric.canvasBackgroundColor" />
</div>
<div class='col-xs-4'>
<label>Canvas Size</label>
<br />
<button ng-hide='canvasCopy' class='btn btn-default btn-block mrm' ng-click='selectCanvas()'>
{{ fabric.canvasOriginalWidth }} x {{ fabric.canvasOriginalHeight }}
</button>
<div class='row' ng-show='canvasCopy'>
<div class='col-xs-12'>
<form ng-submit='setCanvasSize()'>
<div class='form-group'>
<label>Width</label>
<input type='text' ng-model='canvasCopy.width' class='form-control' />
</div>
<div class='form-group'>
<label>Height</label>
<input type='text' ng-model='canvasCopy.height' class='form-control' />
</div>
<button type='submit' class='btn btn-success'>
Submit
</button>
</form>
</div>
</div>
</div>
<div class='col-xs-4'>
<label>&nbsp;</label>
<br />
<div class="btn-group col-xs-12">
<a class="btn btn-block btn-default dropdown-toggle" data-toggle="dropdown" href="#">
Preset Sizes
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-right">
<li ng-click='fabric.setCanvasSize(size.width, size.height); fabric.setDirty(true)' ng-repeat='size in FabricConstants.presetSizes'>
<a>{{ size.name }}</a>
</li>
</ul>
</div>
</div>
</div>
<br />
<div class='row'>
<div class='col-xs-3'>
<div ng-if='fabric.selectedObject'>
<div ng-switch='fabric.selectedObject.type'>
<div ng-switch-when='text'>
<p>
<textarea style="text-align: {{ fabric.selectedObject.textAlign }}" class="form-control" rows="6" ng-model="fabric.selectedObject.text"></textarea>
</p>
<p title="Font size">
<i class="fa fa-text-height"></i>
<input type='number' class="form-control" ng-model="fabric.selectedObject.fontSize" />
</p>
<p title="Line height">
<i class="fa fa-align-left"></i>
<input type='number' class="form-control" ng-model="fabric.selectedObject.lineHeight" step=".1" />
</p>
<div class='btn-group'>
<button ng-class="{ active: fabric.selectedObject.textAlign == 'left' }" ng-click="fabric.selectedObject.textAlign = 'left'" class='btn btn-small'>
<i class='fa fa-align-left'></i>
</button>
<button ng-class="{ active: fabric.selectedObject.textAlign == 'center' }" ng-click="fabric.selectedObject.textAlign = 'center'" class='btn btn-small'>
<i class='fa fa-align-center'></i>
</button>
<button ng-class="{ active: fabric.selectedObject.textAlign == 'right' }" ng-click="fabric.selectedObject.textAlign = 'right'" class='btn btn-small'>
<i class='fa fa-align-right'></i>
</button>
</div>
<br />
<div class='btn-group'>
<button ng-class="{ active: fabric.isBold() }" ng-click="fabric.toggleBold()" class='btn btn-small'>
<i class='fa fa-bold'></i>
</button>
<button ng-class="{ active: fabric.isItalic() }" ng-click="fabric.toggleItalic()" class='btn btn-small'>
<i class='fa fa-italic'></i>
</button>
<button ng-class="{ active: fabric.isUnderline() }" ng-click="fabric.toggleUnderline()" class='btn btn-small'>
<i class='fa fa-underline'></i>
</button>
<button ng-class="{ active: fabric.isLinethrough() }" ng-click="fabric.toggleLinethrough()" class='btn btn-small'>
<i class='fa fa-strikethrough'></i>
</button>
</div>
<br />
<div class="row">
<div class="btn-group col-sm-12">
<a class="btn btn-default btn-block dropdown-toggle" data-toggle="dropdown" href="#">
<span class='object-font-family-preview' style='font-family: "{{ fabric.selectedObject.fontFamily }}";'>
{{ fabric.selectedObject.fontFamily }}
</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat='font in FabricConstants.fonts' ng-click='fabric.selectedObject.fontFamily = font.name' style='font-family: "{{ font.name }}";'>
<a>{{ font.name }}</a>
</li>
</ul>
</div>
</div>
</div>
<div ng-switch-when="image">
<div class="input-prepend">
<button ng-class="{ active: fabric.isTinted() }" ng-click="fabric.toggleTint()" class='btn'>
<i class='fa fa-tint'></i>
</button>
<input type="text" class='input-small' ng-model='fabric.selectedObject.tint' />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<input class='col-sm-12' title="Transparency" type='range'
min="0"
max="1"
step=".01"
ng-model="fabric.selectedObject.opacity" />
</div>
</div>
<div class="row mbm">
<div class="col-sm-12">
<button class='btn btn-small btn-block' ng-class="{ active: fabric.getFlipX() }" ng-click="{ active: fabric.toggleFlipX() }">
<i class='fa fa-exchange'></i> Flip
</button>
</div>
</div>
<div class="row" ng-hide="fabric.selectedObject.type == 'image'">
<div class="col-sm-12">
<input type="text" class='form-control' ng-model="fabric.selectedObject.fill" />
</div>
</div>
<div class="row">
<div class="btn-group col-xs-12 btn-group-vertical">
<button ng-click='fabric.center()' class='btn btn-small btn-block'>
Center
</button>
<button ng-click='fabric.centerH()' class='btn btn-small btn-block'>
Center Horizontally
</button>
<button ng-click='fabric.centerV()' class='btn btn-small btn-block'>
Center Vertically
</button>
</div>
</div>
<br />
<div class="row">
<div class="btn-group col-xs-12 btn-group-vertical">
<button ng-click='fabric.bringToFront(); fabric.setDirty(true)' class='btn btn-small btn-block'>
Bring to front
</button>
<button ng-click='fabric.bringForward(); fabric.setDirty(true)' class='btn btn-small btn-block'>
Bring forwards
</button>
<button ng-click='fabric.sendBackwards(); fabric.setDirty(true)' class='btn btn-small btn-block'>
Send backwards
</button>
<button ng-click='fabric.sendToBack(); fabric.setDirty(true)' class='btn btn-small btn-block'>
Send to back
</button>
</div>
</div>
<br />
<button ng-click='fabric.toggleLockActiveObject(); fabric.setDirty(true)' ng-class="{ active: fabric.selectedObject.lockObject }" class='btn btn-small btn-block'>
Lock
</button>
</div>
</div>
<div class='col-xs-9'>
<div class='image-builder' parent-click="fabric.deactivateAll()">
<div class='fabric-container'>
<canvas fabric='fabric'></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
angular.module('example', [
'common.fabric',
'common.fabric.utilities',
'common.fabric.constants'
])
.controller('ExampleCtrl', ['$scope', 'Fabric', 'FabricConstants', 'Keypress', function($scope, Fabric, FabricConstants, Keypress) {
$scope.fabric = {};
$scope.FabricConstants = FabricConstants;
//
// Creating Canvas Objects
// ================================================================
$scope.addShape = function(path) {
$scope.fabric.addShape('http://fabricjs.com/assets/15.svg');
};
$scope.addImage = function(image) {
$scope.fabric.addImage('http://stargate-sg1-solutions.com/blog/wp-content/uploads/2007/08/daniel-season-nine.jpg');
};
$scope.addImageUpload = function(data) {
var obj = angular.fromJson(data);
$scope.addImage(obj.filename);
};
//
// Editing Canvas Size
// ================================================================
$scope.selectCanvas = function() {
$scope.canvasCopy = {
width: $scope.fabric.canvasOriginalWidth,
height: $scope.fabric.canvasOriginalHeight
};
};
$scope.setCanvasSize = function() {
$scope.fabric.setCanvasSize($scope.canvasCopy.width, $scope.canvasCopy.height);
$scope.fabric.setDirty(true);
delete $scope.canvasCopy;
};
//
// Init
// ================================================================
$scope.init = function() {
$scope.fabric = new Fabric({
JSONExportProperties: FabricConstants.JSONExportProperties,
textDefaults: FabricConstants.textDefaults,
shapeDefaults: FabricConstants.shapeDefaults,
json: {}
});
};
$scope.$on('canvas:created', $scope.init);
Keypress.onSave(function() {
$scope.updatePage();
});
}]);
body {
padding: 10px;
}
.image-builder-container {
position: relative;
.image-builder {
background: #cacaca;
box-sizing: border-box;
border: 1px solid #cacaca;
min-height: 460px;
text-align: center;
position: relative;
overflow-x: scroll;
overflow-y: hidden;
width: auto;
.image-cover {
left: 0;
right: 0;
bottom: 0;
top: 0;
position: absolute;
z-index: 2;
}
.fabric-container {
background: white;
border-radius: 1px;
box-shadow: 0 1px 4px -1px rgba(0, 0, 0, .4);
display: inline-block;
margin: 50px;
position: relative;
vertical-align: middle;
z-index: 0;
}
}
.image-loading {
background: rgba(255, 255, 255, .4);
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
.loading-indicator {
background: white;
background-image: url(/images/squareLoader.gif);
background-repeat: no-repeat;
background-position: center;
border-radius: 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
margin: 300px auto 0;
height: 160px;
width: 160px;
}
}
.object-controls-container {
position: relative;
.object-controls {
position: absolute;
z-index: 1;
background: white;
left: -250px;
top: 0px;
padding: 5px;
width: 240px;
textarea {
font-size: 12px;
}
}
}
}
.object-font-family-preview {
text-transform: capitalize;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment