diff --git "a/js/functions.js" "b/js/functions.js"
new file mode 100644--- /dev/null
+++ "b/js/functions.js"
@@ -0,0 +1,5971 @@
+// Resize the canvas
+function resizeCanvas() {
+ canvas.discardActiveObject();
+ canvas.setHeight($('#canvas-area').height());
+ canvas.setWidth($('#canvas-area').width());
+ artboard.set({
+ left: canvas.get('width') / 2 - artboard.get('width') / 2,
+ top: canvas.get('height') / 2 - artboard.get('height') / 2,
+ });
+ canvas.renderAll();
+ animate(false, currenttime);
+ initLines();
+}
+window.addEventListener('resize', resizeCanvas, false);
+resizeCanvas();
+
+// Highlight layers when selecting objects in the canvas, scroll into view if needed
+function updateSelection(e) {
+ if (e.target.type == 'activeSelection') {
+ $('.layer-selected').removeClass('layer-selected');
+ canvas.getActiveObjects().forEach(function (object) {
+ if (
+ $('.layer').length > 0 &&
+ $(".layer[data-object='" + object.get('id') + "']").length > 0
+ ) {
+ $(".layer[data-object='" + object.get('id') + "']").addClass(
+ 'layer-selected'
+ );
+ if (e.e != undefined) {
+ document
+ .getElementsByClassName('layer-selected')[0]
+ .scrollIntoView();
+ }
+ }
+ });
+ } else {
+ if (
+ $('.layer').length > 0 &&
+ $(".layer[data-object='" + e.target.get('id') + "']").length > 0
+ ) {
+ $('.layer-selected').removeClass('layer-selected');
+ $(".layer[data-object='" + e.target.get('id') + "']").addClass(
+ 'layer-selected'
+ );
+ if (e.e != undefined) {
+ document
+ .getElementsByClassName('layer-selected')[0]
+ .scrollIntoView();
+ }
+ }
+ }
+}
+
+// Object has been modified, automatically add a keyframe
+function autoKeyframe(object, e, multi) {
+ if (e.action == 'drag') {
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe('top', object, currenttime, object.get('top'), true);
+ } else if (e.action == 'scale') {
+ newKeyframe(
+ 'scaleX',
+ object,
+ currenttime,
+ object.get('scaleX'),
+ true
+ );
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe('top', object, currenttime, object.get('top'), true);
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('height'),
+ true
+ );
+ } else if (e.action == 'rotate') {
+ newKeyframe(
+ 'angle',
+ object,
+ currenttime,
+ object.get('angle'),
+ true
+ );
+ if (multi) {
+ newKeyframe(
+ 'scaleX',
+ object,
+ currenttime,
+ object.get('scaleX'),
+ true
+ );
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe(
+ 'top',
+ object,
+ currenttime,
+ object.get('top'),
+ true
+ );
+ }
+ } else if (
+ e.action == 'resizing' ||
+ e.action == 'scaleX' ||
+ e.action == 'scaleY'
+ ) {
+ newKeyframe(
+ 'scaleX',
+ object,
+ currenttime,
+ object.get('scaleX'),
+ true
+ );
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe('top', object, currenttime, object.get('top'), true);
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('height'),
+ true
+ );
+ }
+}
+
+// Reselect
+function reselect(selection) {
+ tempselection = false;
+ if (selection.type == 'activeSelection') {
+ var objs = [];
+ for (let so of selection._objects) {
+ for (let obj of canvas.getObjects()) {
+ if (obj.get('id') === so.get('id')) {
+ objs.push(obj);
+ break;
+ }
+ }
+ }
+ canvas.setActiveObject(
+ new fabric.ActiveSelection(objs, {
+ canvas: canvas,
+ })
+ );
+ canvas.renderAll();
+ } else {
+ if (selection.get('type') == 'group') {
+ canvas.setActiveObject(canvas.getItemById(selection.get('id')));
+ } else {
+ canvas.setActiveObject(selection);
+ }
+ canvas.renderAll();
+ }
+}
+
+// Group objects
+function group() {
+ var objects = canvas.getActiveObjects();
+ var object_ids = [];
+ var newgroup = new fabric.Group();
+ objects.forEach(function (object) {
+ newgroup.addWithUpdate(object);
+ object.set({ inGroup: true });
+ $(".layer[data-object='" + object.get('id') + "']").remove();
+ object_ids.push(object.get('id'));
+ $('#' + object.get('id')).remove();
+ canvas.remove(object);
+ });
+ //var newgroup = canvas.getActiveObject().toGroup();
+ newgroup.set({
+ id: 'Group' + layer_count,
+ objectCaching: false,
+ isGroup: true,
+ color: '#F1890E',
+ type: 'group',
+ stroke: '#000',
+ strokeUniform: true,
+ strokeWidth: 0,
+ paintFirst: 'stroke',
+ absolutePositioned: true,
+ inGroup: false,
+ strokeDashArray: false,
+ objectCaching: true,
+ shadow: {
+ color: 'black',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ groups.push({ id: newgroup.get('id'), objects: object_ids });
+ canvas.renderAll();
+ newLayer(newgroup);
+ canvas.setActiveObject(newgroup);
+ keyframes.sort(function (a, b) {
+ if (a.id.indexOf('Group') >= 0 && b.id.indexOf('Group') == -1) {
+ return 1;
+ } else if (
+ b.id.indexOf('Group') >= 0 &&
+ a.id.indexOf('Group') == -1
+ ) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+ save();
+}
+$(document).on('click', '#group-objects', group);
+
+// Ungroup SVG
+function unGroup(group) {
+ canvas.discardActiveObject();
+ canvas.renderAll();
+ tempgroup = group._objects;
+ group._restoreObjectsState();
+ $(".layer[data-object='" + group.get('id') + "']").remove();
+ $('#' + group.get('id')).remove();
+ canvas.remove(group);
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != group.get('id');
+ });
+ p_keyframes = $.grep(p_keyframes, function (e) {
+ return e.id != group.get('id');
+ });
+ objects = $.grep(objects, function (e) {
+ return e.id != group.get('id');
+ });
+ canvas.renderAll();
+ for (var i = 0; i < tempgroup.length; i++) {
+ if (tempgroup[i].inGroup) {
+ canvas.add(tempgroup[i]);
+ renderLayer(tempgroup[i]);
+ props.forEach(function (prop) {
+ if (
+ prop != 'top' &&
+ prop != 'scaleY' &&
+ prop != 'width' &&
+ prop != 'height' &&
+ prop != 'shadow.offsetX' &&
+ prop != 'shadow.offsetY' &&
+ prop != 'shadow.opacity' &&
+ prop != 'shadow.blur' &&
+ prop != 'lineHeight'
+ ) {
+ renderProp(prop, tempgroup[i]);
+ }
+ });
+ const keyarr = $.grep(keyframes, function (e) {
+ return e.id == tempgroup[i].id;
+ });
+ keyarr.forEach(function (keyframe) {
+ if (
+ keyframe.name != 'top' &&
+ keyframe.name != 'scaleY' &&
+ keyframe.name != 'width' &&
+ keyframe.name != 'height' &&
+ keyframe.name != 'shadow.offsetX' &&
+ keyframe.name != 'shadow.offsetY' &&
+ keyframe.name != 'shadow.opacity' &&
+ keyframe.name != 'shadow.blur' &&
+ keyframe.name != 'lineHeight'
+ ) {
+ renderKeyframe(
+ canvas.getItemById(keyframe.id),
+ keyframe.name,
+ keyframe.t
+ );
+ }
+ });
+ }
+ }
+ canvas.renderAll();
+ save();
+}
+$(document).on('click', '#ungroup-objects', function () {
+ if (canvas.getActiveObject()) {
+ unGroup(canvas.getActiveObject());
+ }
+});
+
+// Regroup SVG
+function reGroup(id) {
+ var group = [];
+ var objects = [];
+ groups
+ .find((x) => x.id == id)
+ .objects.forEach(function (object) {
+ objects.push(canvas.getItemById(object));
+ });
+ var activeselection = new fabric.ActiveSelection(objects);
+ var newgroup = activeselection.toGroup();
+ newgroup.set({
+ id: id,
+ objectCaching: false,
+ });
+ canvas.add(newgroup);
+ canvas.renderAll();
+}
+
+// Keep record canvas up to date
+function updateRecordCanvas() {
+ canvasrecord.setWidth(artboard.width);
+ canvasrecord.setHeight(artboard.height);
+ canvasrecord.width = artboard.width;
+ canvasrecord.height = artboard.height;
+ canvas.clipPath = null;
+ objects.forEach(async function (object) {
+ var obj = canvas.getItemById(object.id);
+ if (obj.filters) {
+ if (obj.filters.length > 0) {
+ object.filters = [];
+ obj.filters.forEach(function (filter) {
+ if (
+ filter.type == 'BlackWhite' ||
+ filter.type == 'Invert' ||
+ filter.type == 'Sepia' ||
+ filter.type == 'Kodachrome' ||
+ filter.type == 'Polaroid' ||
+ filter.type == 'Technicolor' ||
+ filter.type == 'Brownie' ||
+ filter.type == 'Vintage'
+ ) {
+ object.filters.push({ type: filter.type });
+ } else if (filter.type == 'Brightness') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.brightness,
+ });
+ } else if (filter.type == 'Contrast') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.contrast,
+ });
+ } else if (filter.type == 'Vibrance') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.vibrance,
+ });
+ } else if (filter.type == 'Saturation') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.saturation,
+ });
+ } else if (filter.type == 'HueRotation') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.rotation,
+ });
+ } else if (filter.type == 'Blur') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.blur,
+ });
+ } else if (filter.type == 'Noise') {
+ object.filters.push({
+ type: filter.type,
+ value: filter.noise,
+ });
+ } else if (filter.type == 'RemoveColor') {
+ object.filters.push({
+ type: filter.type,
+ distance: filter.distance,
+ color: filter.color,
+ });
+ }
+ });
+ obj.filters = [];
+ obj.applyFilters();
+ var backend = fabric.filterBackend;
+ if (backend && backend.evictCachesForKey) {
+ backend.evictCachesForKey(obj.cacheKey);
+ backend.evictCachesForKey(obj.cacheKey + '_filtered');
+ }
+ if (
+ obj.filters.length > 0 &&
+ obj.get('id').indexOf('Video') >= 0
+ ) {
+ await obj.setElement(obj.saveElem);
+ }
+ } else {
+ object.filters = [];
+ }
+ } else {
+ object.filters = [];
+ }
+ });
+ const canvassave = canvas.toJSON([
+ 'volume',
+ 'audioSrc',
+ 'defaultLeft',
+ 'defaultTop',
+ 'defaultScaleX',
+ 'defaultScaleY',
+ 'notnew',
+ 'starttime',
+ 'top',
+ 'left',
+ 'width',
+ 'height',
+ 'scaleX',
+ 'scaleY',
+ 'flipX',
+ 'flipY',
+ 'originX',
+ 'originY',
+ 'transformMatrix',
+ 'stroke',
+ 'strokeWidth',
+ 'strokeDashArray',
+ 'strokeLineCap',
+ 'strokeDashOffset',
+ 'strokeLineJoin',
+ 'strokeMiterLimit',
+ 'angle',
+ 'opacity',
+ 'fill',
+ 'globalCompositeOperation',
+ 'shadow',
+ 'clipTo',
+ 'visible',
+ 'backgroundColor',
+ 'skewX',
+ 'skewY',
+ 'fillRule',
+ 'paintFirst',
+ 'clipPath',
+ 'strokeUniform',
+ 'rx',
+ 'ry',
+ 'selectable',
+ 'hasControls',
+ 'subTargetCheck',
+ 'id',
+ 'hoverCursor',
+ 'defaultCursor',
+ 'isEditing',
+ 'source',
+ 'assetType',
+ 'duration',
+ 'inGroup',
+ ]);
+ canvas.clipPath = artboard;
+ canvasrecord.loadFromJSON(canvassave, function () {
+ if (canvasrecord.getItemById('center_h')) {
+ canvasrecord.remove(canvasrecord.getItemById('center_h'));
+ canvasrecord.remove(canvasrecord.getItemById('center_v'));
+ }
+ if (canvasrecord.getItemById('line_h')) {
+ canvasrecord.remove(canvasrecord.getItemById('line_h'));
+ canvasrecord.remove(canvasrecord.getItemById('line_v'));
+ }
+ canvasrecord.renderAll();
+ canvasrecord.setWidth(artboard.width);
+ canvasrecord.setHeight(artboard.height);
+ canvasrecord.width = artboard.width;
+ canvasrecord.height = artboard.height;
+ canvasrecord.renderAll();
+ objects.forEach(function (object) {
+ replaceSource(
+ canvasrecord.getItemById(object.id),
+ canvasrecord
+ );
+ replaceSource(canvas.getItemById(object.id), canvas);
+ });
+ });
+}
+
+// Download recording
+function downloadRecording(chunks) {
+ $('#download-real').html('Downloading...');
+ if ($('input[name=radio]:checked').val() == 'webm') {
+ var url = URL.createObjectURL(
+ new Blob(chunks, {
+ type: 'video/webm',
+ })
+ );
+ const a = document.createElement('a');
+ a.style.display = 'none';
+ a.href = url;
+ a.download = name;
+ document.body.appendChild(a);
+ a.click();
+ recording = false;
+ currenttime = 0;
+ animate(false, 0);
+ $('#seekbar').offset({
+ left:
+ offset_left +
+ $('#inner-timeline').offset().left +
+ currenttime / timelinetime,
+ });
+ canvas.renderAll();
+ resizeCanvas();
+ $('#download-real').html('Download');
+ $('#download-real').removeClass('downloading');
+ updateRecordCanvas();
+ } else if ($('input[name=radio]:checked').val() == 'mp4') {
+ type = 'video/mp4';
+ } else {
+ convertStreams(new Blob(chunks, { type: 'video/webm' }), 'gif');
+ }
+}
+
+$('#download-real').on('click', record);
+
+// Save everything in the canvas (for undo/redo/autosave)
+function save() {
+ redo = [];
+ redoarr = [];
+ if (state) {
+ undo.push(state);
+ undoarr.push(statearr);
+ }
+ canvas.clipPath = null;
+ state = canvas.toJSON([
+ 'volume',
+ 'audioSrc',
+ 'top',
+ 'left',
+ 'width',
+ 'height',
+ 'scaleX',
+ 'scaleY',
+ 'flipX',
+ 'flipY',
+ 'originX',
+ 'originY',
+ 'transformMatrix',
+ 'stroke',
+ 'strokeWidth',
+ 'strokeDashArray',
+ 'strokeLineCap',
+ 'strokeDashOffset',
+ 'strokeLineJoin',
+ 'strokeMiterLimit',
+ 'angle',
+ 'opacity',
+ 'fill',
+ 'globalCompositeOperation',
+ 'shadow',
+ 'clipTo',
+ 'visible',
+ 'backgroundColor',
+ 'skewX',
+ 'skewY',
+ 'fillRule',
+ 'paintFirst',
+ 'clipPath',
+ 'strokeUniform',
+ 'rx',
+ 'ry',
+ 'selectable',
+ 'hasControls',
+ 'subTargetCheck',
+ 'id',
+ 'hoverCursor',
+ 'defaultCursor',
+ 'isEditing',
+ 'source',
+ 'assetType',
+ 'duration',
+ 'inGroup',
+ 'filters',
+ ]);
+ canvas.clipPath = artboard;
+ statearr = {
+ keyframes: JSON.parse(JSON.stringify(keyframes)),
+ p_keyframes: JSON.parse(JSON.stringify(p_keyframes)),
+ objects: JSON.parse(JSON.stringify(objects)),
+ colormode: colormode,
+ duration: duration,
+ currenttime: currenttime,
+ };
+ if (undo.length >= 1) {
+ $('#undo').addClass('history-active');
+ } else {
+ $('#undo').removeClass('history-active');
+ }
+ if (redo.length >= 1) {
+ $('#redo').addClass('history-active');
+ } else {
+ $('#redo').removeClass('history-active');
+ }
+
+ updateRecordCanvas();
+ autoSave();
+}
+
+// Duplicate object
+function copyObject() {
+ if (clipboard) {
+ if (cliptype == 'object') {
+ if (clipboard.type == 'activeSelection') {
+ clipboard._objects.forEach(function (clone) {
+ clone.clone(function (cloned) {
+ cloned.set({
+ id: 'Shape' + layer_count,
+ });
+ canvas.add(cloned);
+ canvas.renderAll();
+ newLayer(cloned);
+ canvas.setActiveObject(cloned);
+ });
+ });
+ } else {
+ clipboard.clone(function (cloned) {
+ cloned.set({
+ id: 'Shape' + layer_count,
+ });
+ canvas.add(cloned);
+ canvas.renderAll();
+ newLayer(cloned);
+ canvas.setActiveObject(cloned);
+ });
+ }
+ save();
+ } else {
+ copyKeyframes();
+ }
+ }
+}
+
+// Replace the source of an object when reloading the canvas (since Fabric needs a DOM reference for the objects)
+function replaceSource(object, canvas) {
+ if (object == null) {
+ return false;
+ }
+ if (object.get('type') != 'group') {
+ if (object.type) {
+ if (object.type == 'image') {
+ if (object.get('id').indexOf('Video') >= 0) {
+ var vidObj = document.createElement('video');
+ var vidSrc = document.createElement('source');
+ vidSrc.src = object.get('source');
+ vidObj.crossOrigin = 'anonymous';
+ vidObj.appendChild(vidSrc);
+ vidObj.addEventListener('loadeddata', function () {
+ vidObj.width = this.videoWidth;
+ vidObj.height = this.videoHeight;
+ vidObj.currentTime = 0;
+ vidObj.muted = false;
+ async function waitLoad() {
+ if (vidObj.readyState >= 3) {
+ object.setElement(vidObj);
+ object.saveElem = vidObj;
+ await canvas.renderAll();
+ await animate(false, currenttime);
+ if (
+ objects.find((x) => x.id == object.get('id'))
+ .filters
+ ) {
+ if (
+ objects.find((x) => x.id == object.get('id'))
+ .filters.length > 0
+ ) {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .filters.forEach(function (filter) {
+ if (filter.type == 'Sepia') {
+ object.filters.push(new f.Sepia());
+ } else if (filter.type == 'Invert') {
+ object.filters.push(new f.Invert());
+ } else if (filter.type == 'BlackWhite') {
+ object.filters.push(new f.BlackWhite());
+ } else if (filter.type == 'Kodachrome') {
+ object.filters.push(new f.Kodachrome());
+ } else if (filter.type == 'Polaroid') {
+ object.filters.push(new f.Polaroid());
+ } else if (filter.type == 'Technicolor') {
+ object.filters.push(new f.Technicolor());
+ } else if (filter.type == 'Vintage') {
+ object.filters.push(new f.Vintage());
+ } else if (filter.type == 'Brownie') {
+ object.filters.push(new f.Brownie());
+ } else if (filter.type == 'Brightness') {
+ object.filters.push(
+ new f.Brightness({
+ brightness: filter.value,
+ })
+ );
+ } else if (filter.type == 'Contrast') {
+ object.filters.push(
+ new f.Contrast({ contrast: filter.value })
+ );
+ } else if (filter.type == 'Saturation') {
+ object.filters.push(
+ new f.Saturation({
+ saturation: filter.value,
+ })
+ );
+ } else if (filter.type == 'Vibrance') {
+ object.filters.push(
+ new f.Vibrance({ vibrance: filter.value })
+ );
+ } else if (filter.type == 'HueRotation') {
+ object.filters.push(
+ new f.HueRotation({
+ rotation: filter.value,
+ })
+ );
+ } else if (filter.type == 'Noise') {
+ object.filters.push(
+ new f.Noise({ noise: filter.value })
+ );
+ } else if (filter.type == 'Blur') {
+ object.filters.push(
+ new f.Blur({ blur: filter.value })
+ );
+ } else if (filter.type == 'RemoveColor') {
+ object.filters.push(
+ new f.RemoveColor({
+ distance: filter.distance,
+ color: filter.color,
+ })
+ );
+ }
+ });
+ object.applyFilters();
+ canvas.renderAll();
+ }
+ }
+ } else {
+ window.setTimeout(function () {
+ waitLoad();
+ }, 100);
+ }
+ }
+ window.setTimeout(function () {
+ waitLoad();
+ }, 100);
+ });
+ vidObj.currentTime = 0;
+ } else {
+ //var img = new Image();
+ //img.onload = function(){
+ // object.setElement(img);
+ // canvas.renderAll();
+ if (objects.find((x) => x.id == object.get('id')).filters) {
+ if (
+ objects.find((x) => x.id == object.get('id')).filters
+ .length > 0
+ ) {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .filters.forEach(function (filter) {
+ if (filter.type == 'Sepia') {
+ object.filters.push(new f.Sepia());
+ } else if (filter.type == 'Invert') {
+ object.filters.push(new f.Invert());
+ } else if (filter.type == 'BlackWhite') {
+ object.filters.push(new f.BlackWhite());
+ } else if (filter.type == 'Kodachrome') {
+ object.filters.push(new f.Kodachrome());
+ } else if (filter.type == 'Polaroid') {
+ object.filters.push(new f.Polaroid());
+ } else if (filter.type == 'Technicolor') {
+ object.filters.push(new f.Technicolor());
+ } else if (filter.type == 'Vintage') {
+ object.filters.push(new f.Vintage());
+ } else if (filter.type == 'Brownie') {
+ object.filters.push(new f.Brownie());
+ } else if (filter.type == 'Brightness') {
+ object.filters.push(
+ new f.Brightness({ brightness: filter.value })
+ );
+ } else if (filter.type == 'Contrast') {
+ object.filters.push(
+ new f.Contrast({ contrast: filter.value })
+ );
+ } else if (filter.type == 'Saturation') {
+ object.filters.push(
+ new f.Saturation({ saturation: filter.value })
+ );
+ } else if (filter.type == 'Vibrance') {
+ object.filters.push(
+ new f.Vibrance({ vibrance: filter.value })
+ );
+ } else if (filter.type == 'HueRotation') {
+ object.filters.push(
+ new f.HueRotation({ rotation: filter.value })
+ );
+ } else if (filter.type == 'Noise') {
+ object.filters.push(
+ new f.Noise({ noise: filter.value })
+ );
+ } else if (filter.type == 'Blur') {
+ object.filters.push(
+ new f.Blur({ blur: filter.value })
+ );
+ } else if (filter.type == 'RemoveColor') {
+ object.filters.push(
+ new f.RemoveColor({
+ distance: filter.distance,
+ color: filter.color,
+ })
+ );
+ }
+ });
+ object.applyFilters();
+ canvas.renderAll();
+ } else {
+ object.filters = [];
+ object.applyFilters();
+ canvas.renderAll();
+ }
+ }
+
+ //}
+ //img.src = window.URL.createObjectURL(new Blob(files.find(x => x.name == object.get("id")).file, {type:"image/png"}));
+ }
+ }
+ }
+ }
+}
+
+// Perform undo/redo
+function undoRedo(newState, saveState, newArrState, saveArrState) {
+ saveState.push(state);
+ saveArrState.push(statearr);
+ statearr = newArrState.pop();
+ state = newState.pop();
+ keyframes = statearr.keyframes;
+ p_keyframes = statearr.p_keyframes;
+ objects = statearr.objects;
+ colormode = statearr.colormode;
+ duration = statearr.duration;
+ currenttime = statearr.currenttime;
+ canvas.clipPath = null;
+ canvas.loadFromJSON(state, function () {
+ canvas.clipPath = artboard;
+ canvas.getItemById('line_h').set({ opacity: 0 });
+ canvas.getItemById('line_v').set({ opacity: 0 });
+ canvas.renderAll();
+ $('.object-props').remove();
+ $('.layer').remove();
+ objects.forEach(function (object) {
+ replaceSource(canvas.getItemById(object.id), canvas);
+ renderLayer(canvas.getItemById(object.id));
+ props.forEach(function (prop) {
+ if (
+ prop != 'top' &&
+ prop != 'scaleY' &&
+ prop != 'width' &&
+ prop != 'height' &&
+ prop != 'shadow.offsetX' &&
+ prop != 'shadow.offsetY' &&
+ prop != 'shadow.opacity' &&
+ prop != 'shadow.blur' &&
+ prop != 'lineHeight'
+ ) {
+ renderProp(prop, canvas.getItemById(object.id));
+ }
+ });
+ });
+ keyframes.forEach(function (keyframe) {
+ if (
+ keyframe.name != 'top' &&
+ keyframe.name != 'scaleY' &&
+ keyframe.name != 'width' &&
+ keyframe.name != 'height' &&
+ keyframe.name != 'shadow.offsetX' &&
+ keyframe.name != 'shadow.offsetY' &&
+ keyframe.name != 'shadow.opacity' &&
+ keyframe.name != 'shadow.blur' &&
+ keyframe.name != 'lineHeight'
+ ) {
+ renderKeyframe(
+ canvas.getItemById(keyframe.id),
+ keyframe.name,
+ keyframe.t
+ );
+ }
+ });
+ animate(false, currenttime);
+ });
+ if (undo.length >= 1) {
+ $('#undo').addClass('history-active');
+ } else {
+ $('#undo').removeClass('history-active');
+ }
+ if (redo.length >= 1) {
+ $('#redo').addClass('history-active');
+ } else {
+ $('#redo').removeClass('history-active');
+ }
+}
+
+// Undo/redo buttons
+$(document).on('click', '#undo', function () {
+ if (undo.length >= 1) {
+ undoRedo(undo, redo, undoarr, redoarr);
+ }
+});
+$(document).on('click', '#redo', function () {
+ if (undo.length >= 1) {
+ undoRedo(redo, undo, redoarr, undoarr);
+ }
+});
+
+// Generate keyframes
+function keyframeChanges(object, type, id, selection) {
+ if (object.get('type') == 'rect') {
+ object.set({
+ rx: parseFloat($('#object-corners input').val()),
+ ry: parseFloat($('#object-corners input').val()),
+ });
+ } else if (object.get('type') == 'textbox') {
+ object.set({
+ charSpacing: parseFloat($('#text-h input').val()) * 10,
+ lineHeight: parseFloat($('#text-v input').val() / 100),
+ });
+ canvas.renderAll();
+ }
+ canvas.renderAll();
+ if (type && type == 'opacity') {
+ newKeyframe(
+ 'opacity',
+ object,
+ currenttime,
+ object.get('opacity'),
+ true
+ );
+ } else if (type && type == 'opacity3') {
+ newKeyframe(
+ 'charSpacing',
+ object,
+ currenttime,
+ object.get('charSpacing'),
+ true
+ );
+ newKeyframe(
+ 'lineHeight',
+ object,
+ currenttime,
+ object.get('lineHeight'),
+ true
+ );
+ } else {
+ if (id == 'object-x' || id == 'object-y') {
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe(
+ 'top',
+ object,
+ currenttime,
+ object.get('top'),
+ true
+ );
+ } else if (id == 'object-w' || id == 'object-h') {
+ newKeyframe(
+ 'scaleX',
+ object,
+ currenttime,
+ object.get('scaleX'),
+ true
+ );
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ if (selection) {
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe(
+ 'top',
+ object,
+ currenttime,
+ object.get('top'),
+ true
+ );
+ }
+ } else if (id == 'object-r') {
+ newKeyframe(
+ 'angle',
+ object,
+ currenttime,
+ object.get('angle'),
+ true
+ );
+ if (selection) {
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe(
+ 'top',
+ object,
+ currenttime,
+ object.get('top'),
+ true
+ );
+ newKeyframe(
+ 'scaleX',
+ object,
+ currenttime,
+ object.get('scaleX'),
+ true
+ );
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ }
+ } else if (id == 'object-stroke') {
+ newKeyframe(
+ 'strokeWidth',
+ object,
+ currenttime,
+ object.get('strokeWidth'),
+ true
+ );
+ newKeyframe(
+ 'stroke',
+ object,
+ currenttime,
+ object.get('stroke'),
+ true
+ );
+ } else if (
+ id == 'object-shadow-x' ||
+ id == 'object-shadow-y' ||
+ id == 'object-blur' ||
+ id == 'object-color-stroke-opacity'
+ ) {
+ newKeyframe(
+ 'shadow.color',
+ object,
+ currenttime,
+ object.shadow.color,
+ true
+ );
+ newKeyframe(
+ 'shadow.opacity',
+ object,
+ currenttime,
+ object.shadow.opacity,
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetX',
+ object,
+ currenttime,
+ object.shadow.offsetX,
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetY',
+ object,
+ currenttime,
+ object.shadow.offsetY,
+ true
+ );
+ newKeyframe(
+ 'shadow.blur',
+ object,
+ currenttime,
+ object.shadow.blur,
+ true
+ );
+ }
+ }
+ save();
+}
+
+// Play video
+function play() {
+ paused = false;
+ animate(true, currenttime);
+ $('#play-button').attr('src', 'assets/pause-button.svg');
+}
+
+// Pause video
+function pause() {
+ paused = true;
+ $('#play-button').attr('src', 'assets/play-button.svg');
+}
+
+// Set object value (while animating)
+function setObjectValue(prop, object, value, inst) {
+ if (object.get('type') != 'group') {
+ if (object.group) {
+ var group = object.group;
+ tempgroup = group._objects;
+ group._restoreObjectsState();
+ canvas.setActiveObject(group);
+ inst.remove(canvas.getActiveObject());
+ canvas.discardActiveObject();
+ inst.renderAll();
+ for (var i = 0; i < tempgroup.length; i++) {
+ inst.add(tempgroup[i]);
+ }
+ }
+ }
+ if (prop == 'left' && !recording) {
+ object.set(prop, value + artboard.get('left'));
+ } else if (prop == 'top' && !recording) {
+ object.set(prop, value + artboard.get('top'));
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (prop == 'shadow.color') {
+ object.shadow.color = value;
+ } else if (prop == 'shadow.offsetX') {
+ object.shadow.offsetX = value;
+ } else if (prop == 'shadow.offsetY') {
+ object.shadow.offsetY = value;
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (object.get('type') != 'group') {
+ object.set(prop, value);
+ } else if (prop != 'width') {
+ object.set(prop, value);
+ }
+ inst.renderAll();
+}
+
+// Find last keyframe in time from same object & property
+function lastKeyframe(keyframe, index) {
+ var temparr = keyframes.slice();
+ temparr.sort(function (a, b) {
+ return a.t - b.t;
+ });
+ temparr.length = temparr.findIndex((x) => x === keyframe);
+ temparr.reverse();
+ if (temparr.length == 0) {
+ return false;
+ } else {
+ for (var i = 0; i < temparr.length; i++) {
+ if (
+ temparr[i].id == keyframe.id &&
+ temparr[i].name == keyframe.name
+ ) {
+ return temparr[i];
+ break;
+ } else if (i == temparr.length - 1) {
+ return false;
+ }
+ }
+ }
+}
+
+// Check whether any keyframe exists for a certain property
+function checkAnyKeyframe(id, prop, inst) {
+ const object = inst.getItemById(id);
+ if (object.get('assetType') == 'audio') {
+ return false;
+ }
+ if (
+ object.get('type') != 'textbox' &&
+ (prop == 'charSpacing' || prop == 'lineHeight')
+ ) {
+ return false;
+ }
+ if (
+ object.get('type') == 'group' &&
+ (prop == 'shadow.opacity' ||
+ prop == 'shadow.color' ||
+ prop == 'shadow.offsetX' ||
+ prop == 'shadow.offsetY' ||
+ prop == 'shadow.blur')
+ ) {
+ return false;
+ }
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return e.id == id && e.name == prop;
+ });
+ if (keyarr2.length == 0) {
+ const value = objects
+ .find((x) => x.id == id)
+ .defaults.find((x) => x.name == prop).value;
+ setObjectValue(prop, object, value, inst);
+ }
+}
+
+// Check if parameter is a DOM element
+function isDomElem(el) {
+ return el instanceof HTMLElement || el[0] instanceof HTMLElement
+ ? true
+ : false;
+}
+
+// Play videos when seeking/playing
+async function playVideos(time) {
+ objects.forEach(async function (object) {
+ var object = canvas.getItemById(object.id);
+ if (object == null) {
+ return false;
+ }
+ var inst = canvas;
+ var start = false;
+ if (recording) {
+ object = canvasrecord.getItemById(object.id);
+ inst = canvasrecord;
+ }
+ if (
+ object.get('id').indexOf('Video') >= 0 &&
+ p_keyframes.find((x) => x.id == object.id).trimstart +
+ p_keyframes.find((x) => x.id == object.id).start <=
+ time &&
+ p_keyframes.find((x) => x.id == object.id).end >= time
+ ) {
+ var tempfilters = object.filters;
+ if (object.filters.length > 0) {
+ object.filters = [];
+ object.applyFilters();
+ var image = object;
+ var backend = fabric.filterBackend;
+ if (backend && backend.evictCachesForKey) {
+ backend.evictCachesForKey(image.cacheKey);
+ backend.evictCachesForKey(image.cacheKey + '_filtered');
+ }
+ await object.setElement(object.saveElem);
+ }
+ object.set('visible', true);
+ inst.renderAll();
+ if ($(object.getElement())[0].paused == true) {
+ $(object.getElement())[0].currentTime = parseFloat(
+ (
+ (time -
+ p_keyframes.find((x) => x.id == object.id).start +
+ p_keyframes.find((x) => x.id == object.id).trimstart) /
+ 1000
+ ).toFixed(2)
+ );
+ }
+ if (!recording) {
+ var animation = {
+ value: 0,
+ };
+ var instance = anime({
+ targets: animation,
+ value: [currenttime, duration],
+ delay: 0,
+ duration: duration,
+ easing: 'linear',
+ autoplay: true,
+ update: async function () {
+ if (!paused && start) {
+ if (object.filters.length > 0) {
+ object.filters = [];
+ object.applyFilters();
+ var image = object;
+ var backend = fabric.filterBackend;
+ if (backend && backend.evictCachesForKey) {
+ backend.evictCachesForKey(image.cacheKey);
+ backend.evictCachesForKey(
+ image.cacheKey + '_filtered'
+ );
+ }
+ await object.setElement(object.saveElem);
+ }
+ if ($(object.getElement())[0].tagName == 'VIDEO') {
+ $(object.getElement())[0].play();
+ }
+ await inst.renderAll();
+ if (tempfilters.length > 0) {
+ object.filters = tempfilters;
+ object.applyFilters();
+ inst.renderAll();
+ }
+ } else if (paused) {
+ if (
+ isDomElem($(object.getElement())[0]) &&
+ $(object.getElement())[0].tagName == 'VIDEO'
+ ) {
+ $(object.getElement())[0].pause();
+ }
+ animation.value = duration + 1;
+ anime.remove(animation);
+ }
+ },
+ changeBegin: function () {
+ start = true;
+ },
+ });
+ if (paused) {
+ $(object.getElement())[0].currentTime = parseFloat(
+ (
+ (time -
+ p_keyframes.find((x) => x.id == object.id).start +
+ p_keyframes.find((x) => x.id == object.id)
+ .trimstart) /
+ 1000
+ ).toFixed(2)
+ );
+ await inst.renderAll();
+ if (tempfilters.length > 0) {
+ object.filters = tempfilters;
+ object.applyFilters();
+ inst.renderAll();
+ }
+ }
+ } else {
+ if ($(object.getElement())[0].paused == true) {
+ $(object.getElement())[0].play();
+ inst.renderAll();
+ }
+ }
+ } else if (object.get('id').indexOf('Video') >= 0) {
+ $(object.getElement())[0].pause();
+ object.set('visible', false);
+ inst.renderAll();
+ }
+ });
+}
+
+// Play background audio
+function playAudio(time) {
+ objects.forEach(async function (object) {
+ var start = false;
+ var obj = canvas.getItemById(object.id);
+ if (obj.get('assetType') == 'audio') {
+ var flag = false;
+ var animation = {
+ value: 0,
+ };
+ var instance = anime({
+ targets: animation,
+ value: [currenttime, duration],
+ delay: 0,
+ duration: duration,
+ easing: 'linear',
+ autoplay: true,
+ update: async function () {
+ if (start && play && !paused) {
+ if (
+ !flag &&
+ p_keyframes.find((x) => x.id == object.id).start <=
+ currenttime &&
+ p_keyframes.find((x) => x.id == object.id).end >=
+ currenttime
+ ) {
+ if (obj.get('src')) {
+ obj.get('src').currentTime =
+ (p_keyframes.find((x) => x.id == object.id)
+ .trimstart -
+ p_keyframes.find((x) => x.id == object.id).start +
+ currenttime) /
+ 1000;
+ obj.get('src').volume = obj.get('volume');
+ obj.get('src').play();
+ flag = true;
+ } else {
+ var audio = new Audio(obj.get('audioSrc'));
+ obj.set('src', audio);
+ audio.volume = obj.get('volume');
+ audio.crossOrigin = 'anonymous';
+ audio.currentTime =
+ (p_keyframes.find((x) => x.id == object.id)
+ .trimstart -
+ p_keyframes.find((x) => x.id == object.id).start +
+ currenttime) /
+ 1000;
+ audio.play();
+ flag = true;
+ }
+ } else if (
+ p_keyframes.find((x) => x.id == object.id).start >=
+ currenttime ||
+ p_keyframes.find((x) => x.id == object.id).end <=
+ currenttime
+ ) {
+ if (obj.get('src')) {
+ obj.get('src').pause();
+ }
+ }
+ } else if (paused) {
+ if (obj.get('src')) {
+ obj.get('src').pause();
+ anime.remove(animation);
+ }
+ }
+ },
+ changeBegin: function () {
+ start = true;
+ },
+ });
+ }
+ });
+}
+
+// Temp animate with render callback
+async function recordAnimate(time) {
+ anime.speed = 1;
+ //return new Promise(function(resolve){
+ var inst = canvasrecord;
+ if (animatedtext.length > 0) {
+ animatedtext.forEach(function (text) {
+ text.seek(time, inst);
+ inst.renderAll();
+ });
+ }
+ keyframes.forEach(function (keyframe, index) {
+ // Regroup if needed (groups break to animate their children, then regroup after children have animated)
+ if (groups.find((x) => x.id == keyframe.id)) {
+ if (!canvas.getItemById(keyframe.id)) {
+ reGroup(keyframe.id);
+ }
+ const object = canvas.getItemById(keyframe.id);
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ object.set('visible', false);
+ inst.renderAll();
+ } else if (
+ currenttime >
+ p_keyframes.find((x) => x.id == keyframe.id).end ||
+ currenttime > duration
+ ) {
+ object.set('visible', false);
+ inst.renderAll();
+ } else {
+ object.set('visible', true);
+ inst.renderAll();
+ }
+ if (
+ currenttime >=
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ props.forEach(function (prop) {
+ checkAnyKeyframe(keyframe.id, prop, inst);
+ });
+ }
+ }
+
+ // Copy of setObjectValue function, seems to perform better inside the function
+ function setValue(prop, object, value, inst) {
+ if (object.get('type') != 'group') {
+ if (object.group) {
+ var group = object.group;
+ tempgroup = group._objects;
+ group._restoreObjectsState();
+ canvas.setActiveObject(group);
+ inst.remove(canvas.getActiveObject());
+ canvas.discardActiveObject();
+ inst.renderAll();
+ for (var i = 0; i < tempgroup.length; i++) {
+ inst.add(tempgroup[i]);
+ }
+ }
+ }
+ if (prop == 'left' && !recording) {
+ object.set(prop, value + artboard.get('left'));
+ } else if (prop == 'top' && !recording) {
+ object.set(prop, value + artboard.get('top'));
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (prop == 'shadow.color') {
+ object.shadow.color = value;
+ } else if (prop == 'shadow.offsetX') {
+ object.shadow.offsetX = value;
+ } else if (prop == 'shadow.offsetY') {
+ object.shadow.offsetY = value;
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (object.get('type') != 'group') {
+ object.set(prop, value);
+ } else if (prop != 'width') {
+ object.set(prop, value);
+ }
+ inst.renderAll();
+ }
+
+ // Find next keyframe in time from same object & property
+ function nextKeyframe(keyframe, index) {
+ var temparr = keyframes.slice();
+ temparr.sort(function (a, b) {
+ return a.t - b.t;
+ });
+ temparr.splice(0, temparr.findIndex((x) => x === keyframe) + 1);
+ if (temparr.length == 0) {
+ return false;
+ } else {
+ for (var i = 0; i < temparr.length; i++) {
+ if (
+ temparr[i].id == keyframe.id &&
+ temparr[i].name == keyframe.name
+ ) {
+ return temparr[i];
+ break;
+ } else if (i == temparr.length - 1) {
+ return false;
+ }
+ }
+ }
+ }
+
+ var object = canvasrecord.getItemById(keyframe.id);
+ if (
+ keyframe.t >= time &&
+ currenttime >=
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ var delay = 0;
+ var start = false;
+ var lasttime, lastprop;
+ // Find last keyframe in time from same object & property
+ var lastkey = lastKeyframe(keyframe, index);
+ if (!lastkey) {
+ lasttime = 0;
+ lastprop = objects
+ .find((x) => x.id == keyframe.id)
+ .defaults.find((x) => x.name == keyframe.name).value;
+ } else {
+ lasttime = lastkey.t;
+ lastprop = lastkey.value;
+ }
+ if (lastkey && lastkey.t >= time && !play) {
+ return;
+ }
+
+ // Initiate the animation
+ var animation = {
+ value: lastprop,
+ };
+ var instance = anime({
+ targets: animation,
+ delay: delay,
+ value: keyframe.value,
+ duration: keyframe.t - lasttime,
+ easing: keyframe.easing,
+ autoplay: false,
+ update: function () {
+ if (start && paused) {
+ anime.remove(animation);
+ }
+ },
+ changeBegin: function () {
+ start = true;
+ },
+ });
+
+ if (time - lasttime <= 0) {
+ instance.seek(0);
+ } else {
+ instance.seek(time - lasttime);
+ }
+
+ if (
+ parseFloat(lasttime) <= parseFloat(time) &&
+ parseFloat(keyframe.t) >= parseFloat(time)
+ ) {
+ setValue(keyframe.name, object, animation.value, inst);
+ }
+ } else if (keyframe.t < time && !nextKeyframe(keyframe, index)) {
+ var prop = keyframe.name;
+ if (prop == 'shadow.blur') {
+ if (object.shadow.blur != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.color') {
+ if (object.shadow.color != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.offsetX') {
+ if (object.shadow.offsetX != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.offsetY') {
+ if (object.shadow.offsetY != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else {
+ if (object.get(prop) != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ }
+ }
+ });
+
+ objects.forEach(function (object) {
+ if (object.id.indexOf('Group') == -1) {
+ const object2 = canvas.getItemById(object.id);
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == object.id).trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ object2.set('visible', false);
+ } else if (
+ currenttime >
+ p_keyframes.find((x) => x.id == object.id).end ||
+ currenttime > duration
+ ) {
+ object2.set('visible', false);
+ } else {
+ object2.set('visible', true);
+ }
+ if (
+ currenttime >=
+ p_keyframes.find((x) => x.id == object.id).trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ props.forEach(function (prop) {
+ checkAnyKeyframe(object.id, prop, inst);
+ });
+ }
+ }
+ });
+ inst.renderAll();
+
+ playVideos(time);
+ //});
+}
+
+// Animate timeline (or seek to specific point in time)
+async function animate(play, time) {
+ anime.speed = speed;
+ if (!draggingPanel) {
+ var starttime = new Date();
+ var offset = time;
+ var inst = canvas;
+ keyframes.forEach(function (keyframe, index) {
+ // Find next keyframe in time from same object & property
+ function nextKeyframe(keyframe, index) {
+ var temparr = keyframes.slice();
+ temparr.sort(function (a, b) {
+ return a.t - b.t;
+ });
+ temparr.splice(
+ 0,
+ temparr.findIndex((x) => x === keyframe) + 1
+ );
+ if (temparr.length == 0) {
+ return false;
+ } else {
+ for (var i = 0; i < temparr.length; i++) {
+ if (
+ temparr[i].id == keyframe.id &&
+ temparr[i].name == keyframe.name
+ ) {
+ return temparr[i];
+ break;
+ } else if (i == temparr.length - 1) {
+ return false;
+ }
+ }
+ }
+ }
+ // Regroup if needed (groups break to animate their children, then regroup after children have animated)
+ if (groups.find((x) => x.id == keyframe.id)) {
+ if (!canvas.getItemById(keyframe.id)) {
+ reGroup(keyframe.id);
+ }
+ const object = canvas.getItemById(keyframe.id);
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ object.set('visible', false);
+ inst.renderAll();
+ } else if (
+ currenttime >
+ p_keyframes.find((x) => x.id == keyframe.id).end ||
+ currenttime > duration
+ ) {
+ object.set('visible', false);
+ inst.renderAll();
+ } else {
+ object.set('visible', true);
+ inst.renderAll();
+ }
+ if (
+ currenttime >=
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ props.forEach(function (prop) {
+ checkAnyKeyframe(keyframe.id, prop, inst);
+ });
+ }
+ }
+
+ // Copy of setObjectValue function, seems to perform better inside the function
+ function setValue(prop, object, value, inst) {
+ if (object.get('assetType') == 'audio' && play) {
+ if (object.get('src')) {
+ object.get('src').volume = value;
+ object.set('volume', value);
+ }
+ return false;
+ }
+ if (object.get('type') != 'group') {
+ if (object.group) {
+ /*
+ var group = object.group;
+ tempgroup = group._objects;
+ group._restoreObjectsState();
+ canvas.setActiveObject(group);
+ inst.remove(canvas.getActiveObject());
+ canvas.discardActiveObject();
+ inst.renderAll();
+ for (var i = 0; i < tempgroup.length; i++) {
+ inst.add(tempgroup[i]);
+ }
+ */
+ }
+ }
+ if (prop == 'left' && !recording) {
+ object.set(prop, value + artboard.get('left'));
+ } else if (prop == 'top' && !recording) {
+ object.set(prop, value + artboard.get('top'));
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (prop == 'shadow.color') {
+ object.shadow.color = value;
+ } else if (prop == 'shadow.offsetX') {
+ object.shadow.offsetX = value;
+ } else if (prop == 'shadow.offsetY') {
+ object.shadow.offsetY = value;
+ } else if (prop == 'shadow.blur') {
+ object.shadow.blur = value;
+ } else if (object.get('type') != 'group') {
+ object.set(prop, value);
+ } else if (prop != 'width') {
+ object.set(prop, value);
+ }
+ inst.renderAll();
+ }
+
+ var object = canvas.getItemById(keyframe.id);
+ if (
+ keyframe.t >= time &&
+ currenttime >=
+ p_keyframes.find((x) => x.id == keyframe.id).trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id).start
+ ) {
+ var delay = 0;
+ var start = false;
+ var lasttime, lastprop;
+ // Find last keyframe in time from same object & property
+ var lastkey = lastKeyframe(keyframe, index);
+ if (!lastkey) {
+ lasttime = 0;
+ lastprop = objects
+ .find((x) => x.id == keyframe.id)
+ .defaults.find((x) => x.name == keyframe.name).value;
+ } else {
+ lasttime = lastkey.t;
+ lastprop = lastkey.value;
+ }
+ if (lastkey && lastkey.t >= time && !play) {
+ return;
+ }
+ // Set delay for the animation if playing
+ if (play) {
+ if (lasttime > currenttime) {
+ delay = lasttime - time;
+ }
+ }
+ // Initiate the animation
+ var animation = {
+ value: lastprop,
+ };
+ var instance = anime({
+ targets: animation,
+ delay: delay,
+ value: keyframe.value,
+ duration: keyframe.t - lasttime,
+ easing: keyframe.easing,
+ autoplay: false,
+ update: function () {
+ if (start && !paused) {
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == keyframe.id)
+ .trimstart +
+ p_keyframes.find((x) => x.id == keyframe.id)
+ .start ||
+ currenttime >
+ p_keyframes.find((x) => x.id == keyframe.id).end ||
+ currenttime > duration
+ ) {
+ object.set('visible', false);
+ inst.renderAll();
+ } else {
+ setValue(
+ keyframe.name,
+ object,
+ animation.value,
+ inst
+ );
+ object.set('visible', true);
+ inst.renderAll();
+ }
+ } else if (start && paused) {
+ anime.remove(animation);
+ }
+ },
+ changeBegin: function () {
+ start = true;
+ },
+ });
+
+ if (time - lasttime <= 0) {
+ instance.seek(0);
+ } else {
+ instance.seek(time - lasttime);
+ }
+
+ if (play) {
+ instance.play();
+ } else if (
+ parseFloat(lasttime) <= parseFloat(time) &&
+ parseFloat(keyframe.t) >= parseFloat(time)
+ ) {
+ setValue(keyframe.name, object, animation.value, inst);
+ }
+ } else if (
+ keyframe.t < time &&
+ !nextKeyframe(keyframe, index)
+ ) {
+ var prop = keyframe.name;
+ if (prop == 'left' && !recording) {
+ if (
+ object.get('left') - artboard.get('left') !=
+ keyframe.value
+ ) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'top' && !recording) {
+ if (
+ object.get('top') - artboard.get('top') !=
+ keyframe.value
+ ) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.blur') {
+ if (object.shadow.blur != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.color') {
+ if (object.shadow.color != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.offsetX') {
+ if (object.shadow.offsetX != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else if (prop == 'shadow.offsetY') {
+ if (object.shadow.offsetY != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ } else {
+ if (object.get(prop) != keyframe.value) {
+ setValue(keyframe.name, object, keyframe.value, inst);
+ }
+ }
+ }
+ });
+ /*
+ if (play) {
+ p_keyframes.forEach(function(keyframe){
+ inst.getItemById(keyframe.id).set("visible", false);
+ window.setTimeout(function(){
+ if (!paused) {
+ inst.getItemById(keyframe.id).set("visible", true);
+ inst.renderAll();
+ }
+ }, keyframe.start-time)
+ window.setTimeout(function(){
+ if (!paused) {
+ inst.getItemById(keyframe.id).set("visible", false);
+ inst.renderAll();
+ }
+ }, keyframe.end-time)
+ })
+ }
+ */
+ objects.forEach(function (object) {
+ if (object.id.indexOf('Group') == -1) {
+ const object2 = canvas.getItemById(object.id);
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == object.id).trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ object2.set('visible', false);
+ } else if (
+ currenttime >
+ p_keyframes.find((x) => x.id == object.id).end ||
+ currenttime > duration
+ ) {
+ object2.set('visible', false);
+ } else {
+ object2.set('visible', true);
+ }
+ if (
+ currenttime >=
+ p_keyframes.find((x) => x.id == object.id).trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ props.forEach(function (prop) {
+ checkAnyKeyframe(object.id, prop, inst);
+ });
+ }
+ }
+ var obj = canvas.getItemById(object.id);
+ if (obj.type == 'lottie') {
+ obj.goToSeconds(currenttime);
+ inst.renderAll();
+ }
+ });
+ inst.renderAll();
+
+ if (animatedtext.length > 0) {
+ animatedtext.forEach(function (text) {
+ text.seek(currenttime, canvas);
+ inst.renderAll();
+ });
+ }
+
+ playVideos(time);
+ if (play) {
+ playAudio(time);
+ }
+ if (play && !paused) {
+ var animation = {
+ value: 0,
+ };
+ var main_instance = anime({
+ targets: animation,
+ value: [currenttime, duration],
+ duration: duration - currenttime,
+ easing: 'linear',
+ autoplay: true,
+ update: function () {
+ if (!paused) {
+ currenttime = animation.value;
+ if (animatedtext.length > 0) {
+ animatedtext.forEach(function (text) {
+ text.seek(currenttime, canvas);
+ inst.renderAll();
+ });
+ }
+ objects.forEach(function (object) {
+ if (object.id.indexOf('Group') == -1) {
+ const object2 = inst.getItemById(object.id);
+ if (
+ currenttime <
+ p_keyframes.find((x) => x.id == object.id)
+ .trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ object2.set('visible', false);
+ } else if (
+ currenttime >
+ p_keyframes.find((x) => x.id == object.id).end ||
+ currenttime > duration
+ ) {
+ object2.set('visible', false);
+ } else {
+ object2.set('visible', true);
+ }
+ if (
+ currenttime >=
+ p_keyframes.find((x) => x.id == object.id)
+ .trimstart +
+ p_keyframes.find((x) => x.id == object.id).start
+ ) {
+ props.forEach(function (prop) {
+ checkAnyKeyframe(object.id, prop, inst);
+ });
+ }
+ }
+ var obj = canvas.getItemById(object.id);
+ if (obj.type == 'lottie') {
+ obj.goToSeconds(currenttime);
+ inst.renderAll();
+ }
+ });
+ inst.renderAll();
+ if (!recording) {
+ renderTime();
+ $('#seekbar').css({
+ left: currenttime / timelinetime + offset_left,
+ });
+ }
+ } else {
+ pause();
+ animation.value = duration + 1;
+ anime.remove(animation);
+ }
+ },
+ complete: function () {
+ pause();
+ },
+ });
+ } else if (paused) {
+ currenttime = time;
+ }
+ }
+}
+
+// Render a keyframe
+function renderKeyframe(object, prop, time) {
+ const color = objects.find((x) => x.id == object.id).color;
+ if (prop == 'shadow.color') {
+ if (
+ $('#' + object.get('id'))
+ .find('.shadowcolor')
+ .is(':visible')
+ ) {
+ time =
+ time -
+ parseFloat(
+ p_keyframes.find((x) => x.id == object.get('id')).start
+ );
+ }
+ $('#' + object.get('id'))
+ .find('.shadowcolor')
+ .prepend(
+ "
"
+ );
+ $('#' + object.get('id'))
+ .find('.shadowcolor')
+ .find("[data-time='" + time + "']")
+ .css({ left: time / timelinetime, background: color });
+ } else {
+ if (
+ $('#' + object.get('id'))
+ .find('.' + prop)
+ .is(':visible')
+ ) {
+ time =
+ time -
+ parseFloat(
+ p_keyframes.find((x) => x.id == object.get('id')).start
+ );
+ }
+ $('#' + object.get('id'))
+ .find('.' + prop)
+ .prepend(
+ ""
+ );
+ $('#' + object.get('id'))
+ .find('.' + prop)
+ .find("[data-time='" + time + "']")
+ .css({ left: time / timelinetime, background: color });
+ }
+}
+
+// Create a keyframe
+function newKeyframe(property, object, time, value, render) {
+ // Check if property can be animated
+ if (
+ $.inArray(
+ property,
+ objects.find((x) => x.id == object.get('id')).animate
+ ) != -1
+ ) {
+ const keyarr = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(time) &&
+ e.id == object.get('id') &&
+ e.name == property
+ );
+ });
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return e.id == object.get('id') && e.name == property;
+ });
+ if (keyarr2.length == 0) {
+ if (property == 'left') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value =
+ object.get(property) - artboard.get('left');
+ } else if (property == 'top') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value =
+ object.get(property) - artboard.get('top');
+ } else {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value = value;
+ }
+ }
+ if (keyarr.length == 0) {
+ if (property == 'left') {
+ keyframes.push({
+ t: time,
+ name: property,
+ value: value - artboard.get('left'),
+ id: object.get('id'),
+ easing: 'linear',
+ });
+ } else if (property == 'top') {
+ keyframes.push({
+ t: time,
+ name: property,
+ value: value - artboard.get('top'),
+ id: object.get('id'),
+ easing: 'linear',
+ });
+ } else {
+ keyframes.push({
+ t: time,
+ name: property,
+ value: value,
+ id: object.get('id'),
+ easing: 'linear',
+ });
+ }
+ if (
+ render &&
+ property != 'top' &&
+ property != 'scaleY' &&
+ property != 'width' &&
+ property != 'height' &&
+ property != 'stroke' &&
+ property != 'shadow.opacity' &&
+ property != 'shadow.offsetX' &&
+ property != 'shadow.offsetY' &&
+ property != 'shadow.blur' &&
+ property != 'lineHeight'
+ ) {
+ renderKeyframe(object, property, time);
+ }
+ keyframes.sort(function (a, b) {
+ if (
+ a.id.indexOf('Group') >= 0 &&
+ b.id.indexOf('Group') == -1
+ ) {
+ return 1;
+ } else if (
+ b.id.indexOf('Group') >= 0 &&
+ a.id.indexOf('Group') == -1
+ ) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+ } else if (render) {
+ if (
+ property != 'top' &&
+ property != 'scaleY' &&
+ property != 'width' &&
+ property != 'height' &&
+ property != 'stroke' &&
+ property != 'shadow.opacity' &&
+ property != 'shadow.offsetX' &&
+ property != 'shadow.offsetY' &&
+ property != 'shadow.blur' &&
+ property != 'lineHeight'
+ ) {
+ updateKeyframe(
+ $('#' + object.get('id')).find(
+ ".keyframe[data-time='" +
+ time +
+ "'][data-property='" +
+ property +
+ "']"
+ ),
+ true
+ );
+ }
+ }
+ } else {
+ if (property == 'left') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value =
+ object.get(property) - artboard.get('left');
+ } else if (property == 'top') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value =
+ object.get(property) - artboard.get('top');
+ } else {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == property).value = value;
+ }
+ }
+}
+
+// Create a keyframe (manually)
+function manualKeyframe() {
+ var prop = $(this).parent().attr('data-property');
+ const object = canvas.getItemById(
+ $(this).parent().parent().parent().attr('data-object')
+ );
+ if (prop == 'position') {
+ prop = 'left';
+ newKeyframe('top', object, currenttime, object.get('top'), true);
+ } else if (prop == 'scale') {
+ prop = 'scaleX';
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('height'),
+ true
+ );
+ } else if (prop == 'stroke') {
+ prop = 'strokeWidth';
+ newKeyframe(
+ 'stroke',
+ object,
+ currenttime,
+ object.get('stroke'),
+ true
+ );
+ } else if (prop == 'shadow') {
+ prop = 'shadow.color';
+ newKeyframe(
+ 'shadow.opacity',
+ object,
+ currenttime,
+ object.get('shadow.opacity'),
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetX',
+ object,
+ currenttime,
+ object.get('shadow.offsetX'),
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetY',
+ object,
+ currenttime,
+ object.get('shadow.offsetY'),
+ true
+ );
+ newKeyframe(
+ 'shadow.blur',
+ object,
+ currenttime,
+ object.get('shadow.blur'),
+ true
+ );
+ } else if (prop == 'text') {
+ prop = 'charSpacing';
+ newKeyframe(
+ 'lineHeight',
+ object,
+ currenttime,
+ object.get('lineHeight'),
+ true
+ );
+ }
+ newKeyframe(prop, object, currenttime, object.get(prop), true);
+ save();
+}
+$(document).on('click', '.property-keyframe', manualKeyframe);
+
+// Freeze all properties (this is counterintuitve because I initially programmed it to work the other way around)
+function toggleAnimate(e) {
+ e.stopPropagation();
+ const object = canvas.getItemById(
+ $(this).parent().parent().parent().attr('data-object')
+ );
+ // Turn off clock -> Stop animation
+ if ($(this).hasClass('frozen')) {
+ $(this).removeClass('frozen');
+ $(this).attr('src', 'assets/freeze.svg');
+ objects.find((x) => x.id == object.get('id')).animate = [];
+ // Turn on clock -> Start animation
+ } else {
+ $(this).addClass('frozen');
+ $(this).attr('src', 'assets/frozen.svg');
+ objects.find((x) => x.id == object.get('id')).animate = [];
+ if (object.get('assetType') == 'audio') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('volume');
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id');
+ });
+ $(".keyframe[data-object='" + object.get('id') + "']").remove();
+ $(this)
+ .parent()
+ .parent()
+ .parent()
+ .find('.freeze-prop')
+ .removeClass('frozen');
+ newKeyframe('volume', object, 0, 0.5, true);
+ } else {
+ props.forEach(function (prop) {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push(prop);
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id');
+ });
+ $(".keyframe[data-object='" + object.get('id') + "']").remove();
+ $(this)
+ .parent()
+ .parent()
+ .parent()
+ .find('.freeze-prop')
+ .removeClass('frozen');
+
+ props.forEach(function (prop) {
+ if (prop == 'lineHeight' || prop == 'charSpacing') {
+ if (object.get('type') == 'textbox') {
+ newKeyframe(prop, object, 0, object.get(prop), true);
+ }
+ } else if (
+ prop == 'shadow.opacity' ||
+ prop == 'shadow.blur' ||
+ prop == 'shadow.offsetX' ||
+ prop == 'shadow.offsetY' ||
+ prop == 'shadow.color'
+ ) {
+ if (object.get('type') != 'group') {
+ if (prop == 'shadow.color') {
+ newKeyframe(prop, object, 0, object.shadow.color, true);
+ } else if (prop == 'shadow.opacity') {
+ newKeyframe(
+ prop,
+ object,
+ 0,
+ object.shadow.opacity,
+ true
+ );
+ } else if (prop == 'shadow.offsetX') {
+ newKeyframe(
+ prop,
+ object,
+ 0,
+ object.shadow.offsetX,
+ true
+ );
+ } else if (prop == 'shadow.offsetY') {
+ newKeyframe(
+ prop,
+ object,
+ 0,
+ object.shadow.offsetY,
+ true
+ );
+ } else if (prop == 'shadow.blur') {
+ newKeyframe(prop, object, 0, object.shadow.blur, true);
+ }
+ }
+ } else {
+ newKeyframe(prop, object, 0, object.get(prop), true);
+ }
+ });
+ }
+ }
+ save();
+}
+$(document).on('click', '.freeze', toggleAnimate);
+
+function animateProp(prop, object) {
+ objects.find((x) => x.id == object.get('id')).animate.push(prop);
+
+ // Prop counterparts
+ if (prop == 'left') {
+ objects.find((x) => x.id == object.get('id')).animate.push('top');
+ newKeyframe(
+ 'left',
+ object,
+ currenttime,
+ object.get('left'),
+ true
+ );
+ newKeyframe('top', object, currenttime, object.get('top'), true);
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'left').value =
+ object.get('left') - artboard.get('left');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'top').value =
+ object.get('top') - artboard.get('top');
+ } else if (prop == 'scaleX') {
+ newKeyframe(
+ 'scaleY',
+ object,
+ currenttime,
+ object.get('scaleY'),
+ true
+ );
+ newKeyframe(
+ 'width',
+ object,
+ currenttime,
+ object.get('width'),
+ true
+ );
+ newKeyframe(
+ 'height',
+ object,
+ currenttime,
+ object.get('height'),
+ true
+ );
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('scaleY');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('width');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('height');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'height').value =
+ object.get('height');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'width').value =
+ object.get('width');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'scaleY').value =
+ object.get('scaleY');
+ } else if (prop == 'strokeWidth') {
+ newKeyframe(
+ 'stroke',
+ object,
+ currenttime,
+ object.get('stroke'),
+ true
+ );
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'stroke').value =
+ object.get('stroke');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('stroke');
+ } else if (prop == 'shadow.color') {
+ newKeyframe(
+ 'shadow.color',
+ object,
+ currenttime,
+ object.shadow.color,
+ true
+ );
+ newKeyframe(
+ 'shadow.opacity',
+ object,
+ currenttime,
+ object.shadow.opacity,
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetX',
+ object,
+ currenttime,
+ object.shadow.offsetX,
+ true
+ );
+ newKeyframe(
+ 'shadow.offsetY',
+ object,
+ currenttime,
+ object.shadow.offsetY,
+ true
+ );
+ newKeyframe(
+ 'shadow.blur',
+ object,
+ currenttime,
+ object.shadow.blur,
+ true
+ );
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.color').value =
+ object.get('shadow.color');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.opacity').value =
+ object.get('shadow.opacity');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.offsetX').value =
+ object.get('shadow.offsetX');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.offsetY').value =
+ object.get('shadow.offsetY');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.blur').value =
+ object.get('shadow.blur');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('shadow.opacity');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('shadow.offsetX');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('shadow.offsetY');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('shadow.blur');
+ } else if (prop == 'charSpacing') {
+ newKeyframe(
+ 'lineHeight',
+ object,
+ currenttime,
+ object.get('lineHeight'),
+ true
+ );
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'lineHeight').value =
+ object.get('lineHeight');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .animate.push('lineHeight');
+ }
+
+ // Exception
+ if (prop != 'left' && prop != 'shadow.color') {
+ newKeyframe(prop, object, currenttime, object.get(prop), true);
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == prop).value = object.get(prop);
+ }
+}
+
+function freezeProp(prop, object) {
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != prop;
+ }
+ );
+ // Also add prop counterparts (should probably have done in a better way)
+ if (prop == 'left') {
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'top';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'top';
+ });
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'left').value =
+ object.get('left') - artboard.get('left');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'top').value =
+ object.get('top') - artboard.get('top');
+ } else if (prop == 'scaleX') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'height').value =
+ object.get('height');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'width').value =
+ object.get('width');
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'scaleY').value =
+ object.get('scaleY');
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'scaleY';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'scaleY';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'width';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'width';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'height';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'height';
+ });
+ } else if (prop == 'strokeWidth') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'stroke').value =
+ object.get('stroke');
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'stroke';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'stroke';
+ });
+ } else if (prop == 'shadow.color') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.opacity').value =
+ object.shadow.opacity;
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.offsetX').value =
+ object.shadow.offsetX;
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.offsetY').value =
+ object.shadow.offsetY;
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'shadow.blur').value =
+ object.shadow.blur;
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'shadow.opacity';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'shadow.opacity';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'shadow.offsetX';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'shadow.offsetX';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'shadow.offsetY';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'shadow.offsetY';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'shadow.blur';
+ });
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'shadow.blur';
+ }
+ );
+ } else if (prop == 'charSpacing') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == 'lineHeight').value =
+ object.get('lineHeight');
+ objects.find((x) => x.id == object.get('id')).animate = $.grep(
+ objects.find((x) => x.id == object.get('id')).animate,
+ function (e) {
+ return e != 'lineHeight';
+ }
+ );
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != 'lineHeight';
+ });
+ }
+
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id') || e.name != prop;
+ });
+
+ // Exception
+ if (prop != 'left' && prop != 'shadow.color') {
+ objects
+ .find((x) => x.id == object.get('id'))
+ .defaults.find((x) => x.name == prop).value = object.get(prop);
+ }
+
+ $(
+ ".keyframe[data-object='" +
+ object.get('id') +
+ "'][data-property='" +
+ prop +
+ "']"
+ ).remove();
+}
+
+// Toggle animation mode for property
+function toggleAnimateProp(e) {
+ e.stopPropagation();
+ var prop = $(this).parent().attr('data-property');
+ const object = canvas.getItemById(
+ $(this).parent().parent().parent().attr('data-object')
+ );
+ if (prop == 'position') {
+ prop = 'left';
+ } else if (prop == 'scale') {
+ prop = 'scaleX';
+ } else if (prop == 'stroke') {
+ prop = 'strokeWidth';
+ } else if (prop == 'shadow') {
+ prop = 'shadow.color';
+ } else if (prop == 'text') {
+ prop = 'charSpacing';
+ }
+
+ // Check layer global "freezing" state
+ if (
+ $(this)
+ .parent()
+ .parent()
+ .parent()
+ .find('.freeze')
+ .hasClass('frozen')
+ ) {
+ objects.find((x) => x.id == object.get('id')).animate = [];
+ $(this)
+ .parent()
+ .parent()
+ .parent()
+ .find('.freeze')
+ .removeClass('frozen');
+
+ // Stop animating all props except selected
+ var propmatch = [
+ prop,
+ 'scaleY',
+ 'width',
+ 'height',
+ 'top',
+ 'stroke',
+ 'shadow.opacity',
+ 'shadow.offsetX',
+ 'shadow.offsetY',
+ 'shadow.blur',
+ 'lineHeight',
+ ];
+ props.forEach(function (p) {
+ if ($.inArray(p, propmatch) == -1) {
+ if (
+ (object.get('type') == 'textbox' && p == 'charSpacing') ||
+ p == 'lineHeight'
+ ) {
+ freezeProp(p, object);
+ } else if (p != 'charSpacing' && p != 'lineHeight') {
+ freezeProp(p, object);
+ }
+ }
+ });
+ }
+
+ // Turn off clock -> Stop animating
+ if ($(this).hasClass('frozen')) {
+ $(this).removeClass('frozen');
+ $(this).attr('src', 'assets/freeze.svg');
+ freezeProp(prop, object);
+ // Turn on clock -> Animate
+ } else {
+ $(this).addClass('frozen');
+ $(this).attr('src', 'assets/frozen.svg');
+ animateProp(prop, object);
+ }
+ save();
+}
+$(document).on('click', '.freeze-prop', toggleAnimateProp);
+
+// Lock layer
+function lockLayer(e) {
+ e.stopPropagation();
+ const object = canvas.getItemById(
+ $(this).parent().parent().parent().attr('data-object')
+ );
+ if ($(this).hasClass('locked')) {
+ $(this).removeClass('locked');
+ $(this).attr('src', 'assets/lock.svg');
+ object.selectable = true;
+ $(this).parent().parent().parent().attr('draggable', true);
+ } else {
+ $(this).addClass('locked');
+ $(this).attr('src', 'assets/locked.svg');
+ object.selectable = false;
+ if (canvas.getActiveObject() == object) {
+ canvas.discardActiveObject();
+ canvas.renderAll();
+ }
+ $(this).parent().parent().parent().attr('draggable', false);
+ }
+ save();
+}
+$(document).on('click', '.lock', lockLayer);
+
+// Center an object in the canvas
+function centerObject(object) {
+ object.set('top', artboard.get('top') + artboard.get('height') / 2);
+ object.set(
+ 'left',
+ artboard.get('left') + artboard.get('width') / 2
+ );
+ canvas.renderAll();
+ save();
+}
+
+// Render a layer
+function renderLayer(object, animate = false) {
+ $('#nolayers').addClass('yaylayers');
+ const color = objects.find((x) => x.id == object.get('id')).color;
+ var src = '';
+ var classlock = '';
+ var srclock = 'lock';
+ var freeze = 'freeze';
+ if (object.get('type') == 'textbox') {
+ src = 'assets/text.svg';
+ } else if (
+ object.get('type') == 'rect' ||
+ object.get('type') == 'group' ||
+ object.get('type') == 'circle' ||
+ object.get('type') == 'path'
+ ) {
+ src = 'assets/star.svg';
+ if (object.get('assetType') == 'animatedText') {
+ src = 'assets/text.svg';
+ }
+ if (object.get('assetType') == 'audio') {
+ src = 'assets/audio.svg';
+ }
+ } else if (object.get('type') == 'image') {
+ if (
+ object.get('assetType') &&
+ object.get('assetType') == 'video'
+ ) {
+ src = 'assets/video.svg';
+ } else {
+ src = 'assets/image.svg';
+ }
+ } else if (object.get('type') == 'lottie') {
+ src = 'assets/zappy.svg';
+ } else if (object.get('assetType') == 'audio') {
+ src = 'assets/audio.svg';
+ }
+ if (object.selectable == false) {
+ classlock = 'locked';
+ srclock = 'locked';
+ }
+ if (animate != false) {
+ freeze = 'frozen';
+ }
+ const leftoffset =
+ p_keyframes.find((x) => x.id == object.get('id')).trimstart /
+ timelinetime;
+ const width =
+ (p_keyframes.find((x) => x.id == object.get('id')).end -
+ p_keyframes.find((x) => x.id == object.get('id')).trimstart) /
+ timelinetime;
+ $('#inner-timeline').prepend(
+ " x.id == object.get('id')).start) /
+ timelinetime +
+ "px'>
"
+ );
+ if (object.get('assetType') == 'audio') {
+ object.setControlsVisibility({
+ mt: false,
+ mb: false,
+ ml: false,
+ mr: false,
+ });
+ $('#layer-inner-list').prepend(
+ ""
+ );
+ } else {
+ $('#layer-inner-list').prepend(
+ ""
+ );
+ }
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .toggle();
+ setTimelineZoom(timelinetime);
+ sortable('#layer-inner-list', {
+ placeholderClass: 'hovering',
+ copy: true,
+ customDragImage: (draggedElement, elementOffset, event) => {
+ return {
+ element: document.getElementById('nothing'),
+ posX: event.pageX - elementOffset.left,
+ posY: event.pageY - elementOffset.top,
+ };
+ },
+ });
+ if (object.selectable == false) {
+ $(".layer[data-object='" + object.get('id') + "']").attr(
+ 'draggable',
+ false
+ );
+ }
+}
+
+// Render a property
+function renderProp(prop, object) {
+ var classfreeze = '';
+ srcfreeze = 'freeze';
+ if (
+ $.inArray(
+ prop,
+ objects.find((x) => x.id == object.get('id')).animate
+ ) != -1
+ ) {
+ classfreeze = 'frozen';
+ srcfreeze = 'frozen';
+ }
+ if (prop == 'shadow.color') {
+ prop = 'shadowcolor';
+ }
+ $('#' + object.get('id')).append(
+ ""
+ );
+ if (prop == 'left') {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "Position
"
+ );
+ } else if (prop == 'scaleX') {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "Scale
"
+ );
+ } else if (prop == 'strokeWidth') {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "Stroke
"
+ );
+ } else if (prop == 'shadowcolor') {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "Shadow
"
+ );
+ } else if (prop == 'charSpacing') {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "Text
"
+ );
+ } else {
+ $(".layer[data-object='" + object.get('id') + "']")
+ .find('.properties')
+ .append(
+ "" +
+ prop +
+ "
"
+ );
+ }
+ $('#' + object.get('id'))
+ .find('.keyframe-row' + '.' + prop)
+ .toggle();
+}
+
+// Create a layer
+function newLayer(object) {
+ layer_count++;
+ var color;
+ if (object.get('type') == 'image') {
+ if (
+ object.get('assetType') &&
+ object.get('assetType') == 'video'
+ ) {
+ color = '#106CF6';
+ } else {
+ color = '#92F711';
+ }
+ } else if (object.get('type') == 'textbox') {
+ color = '#F7119B';
+ } else if (
+ object.get('type') == 'rect' ||
+ object.get('type') == 'group' ||
+ object.get('type') == 'circle' ||
+ object.get('type') == 'path'
+ ) {
+ color = '#9211F7';
+ if (object.get('assetType') == 'animatedText') {
+ color = '#F7119B';
+ } else if (object.get('assetType') == 'audio') {
+ color = '#11C0F7';
+ }
+ }
+ if (
+ (object.get('assetType') && object.get('assetType') == 'video') ||
+ object.get('type') == 'lottie' ||
+ object.get('assetType') == 'audio'
+ ) {
+ objects.push({
+ object: object,
+ id: object.get('id'),
+ label: object.get('id'),
+ color: color,
+ defaults: [],
+ locked: [],
+ mask: 'none',
+ start: 0,
+ end: object.get('duration'),
+ });
+ if (object.get('duration') < duration) {
+ p_keyframes.push({
+ start: currenttime,
+ end: object.get('duration') + currenttime,
+ trimstart: 0,
+ trimend: object.get('duration') + currenttime,
+ object: object,
+ id: object.get('id'),
+ });
+ } else {
+ p_keyframes.push({
+ start: currenttime,
+ end: duration - currenttime,
+ trimstart: 0,
+ trimend: duration - currenttime,
+ object: object,
+ id: object.get('id'),
+ });
+ }
+ } else {
+ objects.push({
+ object: object,
+ id: object.get('id'),
+ label: object.get('id'),
+ color: color,
+ defaults: [],
+ locked: [],
+ mask: 'none',
+ });
+ if (object.get('notnew')) {
+ p_keyframes.push({
+ start: object.get('starttime'),
+ end: duration - object.get('starttime'),
+ trimstart: 0,
+ trimend: duration - currenttime,
+ object: object,
+ id: object.get('id'),
+ });
+ } else {
+ p_keyframes.push({
+ start: currenttime,
+ end: duration - currenttime,
+ trimstart: 0,
+ trimend: duration - currenttime,
+ object: object,
+ id: object.get('id'),
+ });
+ }
+ }
+ renderLayer(object);
+ if (
+ !object.get('assetType') ||
+ object.get('assetType') != 'audio'
+ ) {
+ props.forEach(function (prop) {
+ if (prop == 'lineHeight' || prop == 'charSpacing') {
+ if (object.get('type') == 'textbox') {
+ if (prop != 'lineHeight') {
+ renderProp(prop, object);
+ }
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({ name: prop, value: object.get(prop) });
+ }
+ } else if (
+ prop == 'shadow.opacity' ||
+ prop == 'shadow.blur' ||
+ prop == 'shadow.offsetX' ||
+ prop == 'shadow.offsetY' ||
+ prop == 'shadow.color'
+ ) {
+ if (object.get('type') != 'group') {
+ if (prop == 'shadow.color') {
+ renderProp(prop, object);
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({
+ name: prop,
+ value: object.shadow.color,
+ });
+ } else if (prop == 'shadow.blur') {
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({
+ name: prop,
+ value: object.shadow.blur,
+ });
+ } else if (prop == 'shadow.offsetX') {
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({
+ name: prop,
+ value: object.shadow.offsetX,
+ });
+ } else if (prop == 'shadow.offsetY') {
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({
+ name: prop,
+ value: object.shadow.offsetY,
+ });
+ } else if (prop == 'shadow.opacity') {
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({
+ name: prop,
+ value: object.shadow.opacity,
+ });
+ }
+ }
+ } else {
+ if (
+ prop != 'top' &&
+ prop != 'scaleY' &&
+ prop != 'stroke' &&
+ prop != 'width' &&
+ prop != 'height'
+ ) {
+ renderProp(prop, object);
+ }
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({ name: prop, value: object.get(prop) });
+ }
+ });
+ } else {
+ renderProp('volume', object);
+ objects
+ .find((x) => x.id == object.id)
+ .defaults.push({ name: 'volume', value: 0 });
+ }
+ $('.layer-selected').removeClass('layer-selected');
+ $(".layer[data-object='" + object.get('id') + "']").addClass(
+ 'layer-selected'
+ );
+ document
+ .getElementsByClassName('layer-selected')[0]
+ .scrollIntoView();
+ objects.find((x) => x.id == object.id).animate = [];
+ animate(false, currenttime);
+ save();
+ checkFilter();
+}
+
+// Add a (complex) SVG shape to the canvas
+function newSVG(svg, x, y, width, center) {
+ var svggroup = [];
+ fabric.loadSVGFromURL(svg, function (objects, options) {
+ var newsvg = objects[0];
+ if (objects.length > 1) {
+ newsvg = fabric.util.groupSVGElements(objects, options);
+ }
+ newsvg.set({
+ id: 'Shape' + layer_count,
+ stroke: '#000',
+ left: x,
+ top: y,
+ strokeWidth: 0,
+ strokeUniform: true,
+ originX: 'center',
+ originY: 'center',
+ strokeDashArray: false,
+ absolutePositioned: true,
+ paintFirst: 'stroke',
+ objectCaching: true,
+ sourcePath: svg,
+ inGroup: false,
+ shadow: {
+ color: '#000',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ newsvg.scaleToWidth(width);
+ newsvg.set({
+ scaleX: parseFloat(newsvg.get('scaleX').toFixed(2)),
+ scaleY: parseFloat(newsvg.get('scaleY').toFixed(2)),
+ });
+ canvas.add(newsvg);
+ newLayer(newsvg);
+ canvas.setActiveObject(newsvg);
+ canvas.bringToFront(newsvg);
+ canvas.renderAll();
+ if (center) {
+ newsvg.set(
+ 'left',
+ artboard.get('left') + artboard.get('width') / 2
+ );
+ newsvg.set(
+ 'top',
+ artboard.get('top') + artboard.get('height') / 2
+ );
+ canvas.renderAll();
+ }
+ });
+}
+
+// Add a video to the canvas
+function newVideo(file, src, x, y, duration, center) {
+ var newvid = new fabric.Image(file, {
+ left: x,
+ top: y,
+ width: file.width,
+ height: file.height,
+ originX: 'center',
+ originY: 'center',
+ backgroundColor: 'rgba(255,255,255,0)',
+ cursorWidth: 1,
+ stroke: '#000',
+ strokeUniform: true,
+ paintFirst: 'stroke',
+ strokeWidth: 0,
+ cursorDuration: 1,
+ cursorDelay: 250,
+ source: src,
+ duration: duration * 1000,
+ assetType: 'video',
+ id: 'Video' + layer_count,
+ objectCaching: false,
+ strokeDashArray: false,
+ inGroup: false,
+ shadow: {
+ color: '#000',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ files.push({ name: newvid.get('id'), file: src });
+ newvid.saveElem = newvid.getElement();
+ canvas.add(newvid);
+ if (newvid.get('width') > artboard.get('width')) {
+ newvid.scaleToWidth(artboard.get('width'));
+ }
+ newvid.scaleToWidth(150);
+ canvas.renderAll();
+ if (window.duration < newvid.duration + currenttime) {
+ window.duration =
+ ((newvid.duration + currenttime) / 1000).toFixed(2) * 1000;
+ }
+ newLayer(newvid);
+ canvas.setActiveObject(newvid);
+ canvas.bringToFront(newvid);
+ if (center) {
+ newvid.set(
+ 'left',
+ artboard.get('left') + artboard.get('width') / 2
+ );
+ newvid.set(
+ 'top',
+ artboard.get('top') + artboard.get('height') / 2
+ );
+ canvas.renderAll();
+ }
+ $('#load-video').removeClass('loading-active');
+}
+
+// Load a video
+function loadVideo(src, x, y, center) {
+ var vidObj = document.createElement('video');
+ var vidSrc = document.createElement('source');
+ vidSrc.src = src;
+ vidObj.crossOrigin = 'anonymous';
+ vidObj.appendChild(vidSrc);
+ vidObj.addEventListener('loadeddata', function () {
+ vidObj.width = this.videoWidth;
+ vidObj.height = this.videoHeight;
+ vidObj.currentTime = 0;
+ vidObj.muted = false;
+ function waitLoad() {
+ if (vidObj.readyState >= 3) {
+ newVideo(vidObj, src, x, y, vidObj.duration, center);
+ } else {
+ window.setTimeout(function () {
+ waitLoad();
+ }, 100);
+ }
+ }
+ window.setTimeout(function () {
+ waitLoad();
+ }, 100);
+ });
+ vidObj.currentTime = 0;
+}
+
+// Check that crop controls are inside image
+function checkCrop(obj) {
+ if (obj.isContainedWithinObject(cropobj)) {
+ croptop = obj.get('top');
+ cropleft = obj.get('left');
+ cropscalex = obj.get('scaleX');
+ cropscaley = obj.get('scaleY');
+ } else {
+ obj.top = croptop;
+ obj.left = cropleft;
+ obj.scaleX = cropscalex;
+ obj.scaleY = cropscaley;
+ obj.setCoords();
+ obj.saveState();
+ }
+ obj.set({
+ borderColor: '#51B9F9',
+ });
+ canvas.renderAll();
+ crop(canvas.getItemById('cropped'));
+}
+
+// Perform a crop
+function crop(obj) {
+ var crop = canvas.getItemById('crop');
+ cropobj.setCoords();
+ crop.setCoords();
+ var cleft =
+ crop.get('left') - (crop.get('width') * crop.get('scaleX')) / 2;
+ var ctop =
+ crop.get('top') - (crop.get('height') * crop.get('scaleY')) / 2;
+ var height =
+ (crop.get('height') / cropobj.get('scaleY')) * crop.get('scaleY');
+ var width =
+ (crop.get('width') / cropobj.get('scaleX')) * crop.get('scaleX');
+ var img_height = cropobj.get('height') * cropobj.get('scaleY');
+ var img_width = cropobj.get('width') * cropobj.get('scaleX');
+ var left =
+ cleft -
+ (cropobj.get('left') -
+ (cropobj.get('width') * cropobj.get('scaleX')) / 2);
+ var top =
+ ctop -
+ (cropobj.get('top') -
+ (cropobj.get('height') * cropobj.get('scaleY')) / 2);
+ if (left < 0 && top > 0) {
+ obj
+ .set({ cropY: top / cropobj.get('scaleY'), height: height })
+ .setCoords();
+ canvas.renderAll();
+ obj.set({
+ top: ctop + (obj.get('height') * obj.get('scaleY')) / 2,
+ });
+ canvas.renderAll();
+ } else if (top < 0 && left > 0) {
+ obj
+ .set({ cropX: left / cropobj.get('scaleX'), width: width })
+ .setCoords();
+ canvas.renderAll();
+ obj.set({
+ left: cleft + (obj.get('width') * obj.get('scaleX')) / 2,
+ });
+ canvas.renderAll();
+ } else if (top > 0 && left > 0) {
+ obj
+ .set({
+ cropX: left / cropobj.get('scaleX'),
+ cropY: top / cropobj.get('scaleY'),
+ height: height,
+ width: width,
+ })
+ .setCoords();
+ canvas.renderAll();
+ obj.set({
+ left: cleft + (obj.get('width') * obj.get('scaleX')) / 2,
+ top: ctop + (obj.get('height') * obj.get('scaleY')) / 2,
+ });
+ canvas.renderAll();
+ }
+ if (obj.get('id') != 'cropped') {
+ canvas.remove(crop);
+ canvas.remove(canvas.getItemById('overlay'));
+ canvas.remove(canvas.getItemById('cropped'));
+ cropping = false;
+ resetControls();
+ canvas.uniformScaling = true;
+ canvas.renderAll();
+ newKeyframe('scaleX', obj, currenttime, obj.get('scaleX'), true);
+ newKeyframe('scaleY', obj, currenttime, obj.get('scaleY'), true);
+ newKeyframe('width', obj, currenttime, obj.get('width'), true);
+ newKeyframe('height', obj, currenttime, obj.get('width'), true);
+ newKeyframe('left', obj, currenttime, obj.get('left'), true);
+ newKeyframe('top', obj, currenttime, obj.get('top'), true);
+ $('#properties-overlay').removeClass('properties-disabled');
+ save();
+ }
+ canvas.renderAll();
+}
+
+var tlcrop = new Image();
+tlcrop.src = 'assets/tlcrop.svg';
+var trcrop = new Image();
+trcrop.src = 'assets/trcrop.svg';
+var blcrop = new Image();
+blcrop.src = 'assets/blcrop.svg';
+var brcrop = new Image();
+brcrop.src = 'assets/brcrop.svg';
+
+function overlay() {
+ canvas.add(
+ new fabric.Rect({
+ left: artboard.left,
+ top: artboard.top,
+ originX: 'left',
+ originY: 'top',
+ width: artboard.width,
+ height: artboard.height,
+ fill: 'rgba(0,0,0,0.5)',
+ selectable: false,
+ id: 'overlay',
+ })
+ );
+}
+
+// Start cropping an image
+function cropImage(object) {
+ if (!cropping) {
+ $('#properties-overlay').addClass('properties-disabled');
+ cropping = true;
+ cropobj = object;
+ canvas.uniformScaling = false;
+ cropobj.setCoords();
+ var left =
+ cropobj.get('left') -
+ (cropobj.get('width') * cropobj.get('scaleX')) / 2;
+ var top =
+ cropobj.get('top') -
+ (cropobj.get('height') * cropobj.get('scaleY')) / 2;
+ var cropx = cropobj.get('cropX');
+ var cropy = cropobj.get('cropY');
+ overlay();
+ var cropUI = new fabric.Rect({
+ left: object.get('left'),
+ top: object.get('top'),
+ width: object.get('width') * object.get('scaleX') - 5,
+ height: object.get('height') * object.get('scaleY') - 5,
+ originX: 'center',
+ originY: 'center',
+ id: 'crop',
+ fill: 'rgba(0,0,0,0)',
+ shadow: {
+ color: 'black',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ cropobj.clone(function (cloned) {
+ cloned.set({
+ id: 'cropped',
+ selectable: false,
+ originX: 'center',
+ originY: 'center',
+ });
+ canvas.add(cloned);
+ canvas.bringToFront(cloned);
+ canvas.bringToFront(cropUI);
+ canvas.renderAll();
+ cropobj = object;
+ });
+ cropobj
+ .set({
+ cropX: 0,
+ cropY: 0,
+ width: cropobj.get('ogWidth'),
+ height: cropobj.get('ogHeight'),
+ })
+ .setCoords();
+ canvas.renderAll();
+ cropobj.set({
+ left:
+ left +
+ (cropobj.get('width') * cropobj.get('scaleX')) / 2 -
+ cropx * cropobj.get('scaleX'),
+ top:
+ top +
+ (cropobj.get('height') * cropobj.get('scaleY')) / 2 -
+ cropy * cropobj.get('scaleY'),
+ });
+ cropUI.setControlsVisibility({
+ mt: false,
+ mb: false,
+ mr: false,
+ ml: false,
+ mtr: false,
+ });
+ cropUI.controls.tl = new fabric.Control({
+ x: -0.5,
+ y: -0.5,
+ offsetX: 3,
+ offsetY: 3,
+ cursorStyleHandler:
+ fabric.controlsUtils.scaleCursorStyleHandler,
+ actionHandler: fabric.controlsUtils.scalingEqually,
+ render: function (ctx, left, top, styleOverride, fabricObject) {
+ const wsize = 27;
+ const hsize = 27;
+ ctx.save();
+ ctx.translate(left, top);
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
+ ctx.drawImage(tlcrop, -wsize / 2, -hsize / 2, wsize, hsize);
+ ctx.restore();
+ },
+ });
+ cropUI.controls.tr = new fabric.Control({
+ x: 0.5,
+ y: -0.5,
+ offsetX: -3,
+ offsetY: 3,
+ cursorStyleHandler:
+ fabric.controlsUtils.scaleCursorStyleHandler,
+ actionHandler: fabric.controlsUtils.scalingEqually,
+ render: function (ctx, left, top, styleOverride, fabricObject) {
+ const wsize = 27;
+ const hsize = 27;
+ ctx.save();
+ ctx.translate(left, top);
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
+ ctx.drawImage(trcrop, -wsize / 2, -hsize / 2, wsize, hsize);
+ ctx.restore();
+ },
+ });
+ cropUI.controls.bl = new fabric.Control({
+ x: -0.5,
+ y: 0.5,
+ offsetX: 3,
+ offsetY: -3,
+ cursorStyleHandler:
+ fabric.controlsUtils.scaleCursorStyleHandler,
+ actionHandler: fabric.controlsUtils.scalingEqually,
+ render: function (ctx, left, top, styleOverride, fabricObject) {
+ const wsize = 27;
+ const hsize = 27;
+ ctx.save();
+ ctx.translate(left, top);
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
+ ctx.drawImage(blcrop, -wsize / 2, -hsize / 2, wsize, hsize);
+ ctx.restore();
+ },
+ });
+ cropUI.controls.br = new fabric.Control({
+ x: 0.5,
+ y: 0.5,
+ offsetX: -3,
+ offsetY: -3,
+ cursorStyleHandler:
+ fabric.controlsUtils.scaleCursorStyleHandler,
+ actionHandler: fabric.controlsUtils.scalingEqually,
+ render: function (ctx, left, top, styleOverride, fabricObject) {
+ const wsize = 27;
+ const hsize = 27;
+ ctx.save();
+ ctx.translate(left, top);
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
+ ctx.drawImage(brcrop, -wsize / 2, -hsize / 2, wsize, hsize);
+ ctx.restore();
+ },
+ });
+ canvas.add(cropUI);
+ canvas.setActiveObject(cropUI);
+ canvas.renderAll();
+ cropleft = cropUI.get('left');
+ croptop = cropUI.get('top');
+ cropscalex = cropUI.get('scaleX') - 0.03;
+ cropscaley = cropUI.get('scaleY') - 0.03;
+ }
+}
+$(document).on('click', '#crop-image', function () {
+ if (canvas.getActiveObject()) {
+ cropImage(canvas.getActiveObject());
+ }
+});
+
+// Add an image to the canvas
+function newImage(file, x, y, width, center) {
+ var newimg = new fabric.Image(file, {
+ left: x,
+ top: y,
+ originX: 'center',
+ originY: 'center',
+ stroke: '#000',
+ strokeUniform: true,
+ strokeWidth: 0,
+ paintFirst: 'stroke',
+ absolutePositioned: true,
+ id: 'Image' + layer_count,
+ inGroup: false,
+ strokeDashArray: false,
+ objectCaching: true,
+ shadow: {
+ color: 'black',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ files.push({ name: newimg.get('id'), file: file.src });
+ canvas.add(newimg);
+ newimg.scaleToWidth(width);
+ newimg.set({
+ scaleX: parseFloat(newimg.get('scaleX').toFixed(2)),
+ scaleY: parseFloat(newimg.get('scaleY').toFixed(2)),
+ ogWidth: newimg.get('width'),
+ ogHeight: newimg.get('height'),
+ });
+ canvas.bringToFront(newimg);
+ canvas.renderAll();
+ newLayer(newimg);
+ canvas.setActiveObject(newimg);
+ if (center) {
+ newimg.set(
+ 'left',
+ artboard.get('left') + artboard.get('width') / 2
+ );
+ newimg.set(
+ 'top',
+ artboard.get('top') + artboard.get('height') / 2
+ );
+ canvas.renderAll();
+ }
+ $('#load-image').removeClass('loading-active');
+}
+
+function loadImage(src, x, y, width, center) {
+ var image = new Image();
+ image.onload = function (img) {
+ newImage(image, x, y, width, center);
+ };
+ image.src = src;
+}
+
+function createVideoThumbnail(file, max, seekTo = 0.0, isURL) {
+ return new Promise((resolve, reject) => {
+ const videoPlayer = document.createElement('video');
+ if (isURL) {
+ videoPlayer.setAttribute('src', file);
+ } else {
+ videoPlayer.setAttribute('src', URL.createObjectURL(file));
+ }
+ videoPlayer.setAttribute('crossorigin', 'anonymous');
+ videoPlayer.load();
+ videoPlayer.addEventListener('error', (ex) => {
+ reject('error when loading video file', ex);
+ });
+ videoPlayer.addEventListener('loadedmetadata', () => {
+ if (videoPlayer.duration < seekTo) {
+ reject('video is too short.');
+ return;
+ }
+ setTimeout(() => {
+ videoPlayer.currentTime = seekTo;
+ }, 200);
+ videoPlayer.addEventListener('seeked', () => {
+ var oc = document.createElement('canvas');
+ var octx = oc.getContext('2d');
+ oc.width = videoPlayer.videoWidth;
+ oc.height = videoPlayer.videoheight;
+ octx.drawImage(videoPlayer, 0, 0);
+ if (videoPlayer.videoWidth > videoPlayer.videoHeight) {
+ oc.height =
+ (videoPlayer.videoHeight / videoPlayer.videoWidth) * max;
+ oc.width = max;
+ } else {
+ oc.width =
+ (videoPlayer.videoWidth / videoPlayer.videoHeight) * max;
+ oc.height = max;
+ }
+ octx.drawImage(oc, 0, 0, oc.width, oc.height);
+ octx.drawImage(videoPlayer, 0, 0, oc.width, oc.height);
+ resolve(oc.toDataURL());
+ });
+ });
+ });
+}
+
+function createThumbnail(file, max) {
+ return new Promise(function (resolve, reject) {
+ var reader = new FileReader();
+ reader.onload = function (event) {
+ var img = new Image();
+ img.onload = function () {
+ if (img.width > max) {
+ var oc = document.createElement('canvas');
+ var octx = oc.getContext('2d');
+ oc.width = img.width;
+ oc.height = img.height;
+ octx.drawImage(img, 0, 0);
+ if (img.width > img.height) {
+ oc.height = (img.height / img.width) * max;
+ oc.width = max;
+ } else {
+ oc.width = (img.width / img.height) * max;
+ oc.height = max;
+ }
+ octx.drawImage(oc, 0, 0, oc.width, oc.height);
+ octx.drawImage(img, 0, 0, oc.width, oc.height);
+ resolve(oc.toDataURL());
+ } else {
+ resolve(img.src);
+ }
+ };
+ img.src = event.target.result;
+ };
+ reader.readAsDataURL(file);
+ });
+}
+
+function dataURItoBlob(dataURI) {
+ var byteString = atob(dataURI.split(',')[1]);
+ var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
+ var ab = new ArrayBuffer(byteString.length);
+ var ia = new Uint8Array(ab);
+ for (var i = 0; i < byteString.length; i++) {
+ ia[i] = byteString.charCodeAt(i);
+ }
+ var blob = new Blob([ab], { type: mimeString });
+ return blob;
+}
+
+async function uploadFromURL() {
+ var url = $('#upload-link-input').val();
+ let file = await fetch(url).then((r) => r.blob());
+ if (file.type.split('/')[0] === 'image') {
+ $('#upload-link-input').val('');
+ $('.upload-show').removeClass('upload-show');
+ createThumbnail(file, 250).then(function (data) {
+ saveFile(
+ dataURItoBlob(data),
+ file,
+ file.type.split('/')[0],
+ 'temp',
+ false,
+ false
+ );
+ });
+ } else if (file.type.split('/')[0] === 'video') {
+ $('.upload-show').removeClass('upload-show');
+ createVideoThumbnail(file, 250, 0, false).then(function (data) {
+ saveFile(
+ dataURItoBlob(data),
+ file,
+ file.type.split('/')[0],
+ 'temp',
+ false,
+ false
+ );
+ });
+ $('#upload-link-input').val('');
+ } else {
+ alert('File type not accepted');
+ }
+}
+$(document).on('click', '#upload-link-add', uploadFromURL);
+
+function handleUpload(custom = false) {
+ var files2;
+ if (custom == false) {
+ files2 = $('#filepick').get(0).files;
+ } else {
+ files2 = custom.originalEvent.dataTransfer.files;
+ }
+ if (files2) {
+ Array.from(files2).forEach((file) => {
+ uploading = true;
+ if (file.size / 1024 / 1024 <= 10) {
+ $('#upload-button').html(
+ " Uploading..."
+ );
+ $('#upload-button').addClass('uploading');
+ if (file['type'].split('/')[0] === 'image') {
+ $('.upload-show').removeClass('upload-show');
+ createThumbnail(file, 250).then(function (data) {
+ saveFile(
+ dataURItoBlob(data),
+ file,
+ file['type'].split('/')[0],
+ 'temp',
+ false,
+ false
+ );
+ });
+ } else if (file['type'].split('/')[0] === 'video') {
+ $('.upload-show').removeClass('upload-show');
+ createVideoThumbnail(file, 250, 0, false).then(function (
+ data
+ ) {
+ saveFile(
+ dataURItoBlob(data),
+ file,
+ file['type'].split('/')[0],
+ 'temp',
+ false,
+ false
+ );
+ });
+ } else {
+ alert('File type not accepted');
+ }
+ } else {
+ alert('File is too big');
+ }
+ });
+ if (files2.length == 1) {
+ if (files2[0]['type'].split('/')[0] === 'image') {
+ $('.upload-tab-active').removeClass('upload-tab-active');
+ $('#images-tab').addClass('upload-tab-active');
+ } else if (files2[0]['type'].split('/')[0] === 'video') {
+ $('.upload-tab-active').removeClass('upload-tab-active');
+ $('#videos-tab').addClass('upload-tab-active');
+ } else if (files2[0]['type'].split('/')[0] === 'audio') {
+ $('.upload-tab-active').removeClass('upload-tab-active');
+ $('#audio-tab').addClass('upload-tab-active');
+ }
+ }
+ }
+}
+$(document).on('change', '#filepick', function () {
+ handleUpload(false);
+});
+
+// Upload audio
+function audioUpload() {
+ const files = $('#filepick2').get(0).files;
+ if (files) {
+ if (files.length == 1) {
+ if (files[0]['type'].split('/')[0] === 'audio') {
+ if (files[0].size / 1024 / 1024 <= 10) {
+ $('#audio-upload-button').html('Uploading...');
+ $('#audio-upload-button').addClass('uploading');
+ saveAudio(files[0]);
+ } else {
+ alert('File is too big');
+ }
+ } else {
+ alert('Wrong file type');
+ }
+ }
+ }
+}
+$(document).on('change', '#filepick2', audioUpload);
+
+// Create a rectangle
+function newRectangle(color) {
+ var newrect = new fabric.Rect({
+ left: 0,
+ top: 0,
+ originX: 'center',
+ originY: 'center',
+ width: 200,
+ height: 200,
+ stroke: '#000',
+ strokeWidth: 0,
+ strokeUniform: true,
+ backgroundColor: 'rgba(255,255,255,0)',
+ rx: 0,
+ ry: 0,
+ fill: color,
+ cursorWidth: 1,
+ cursorDuration: 1,
+ paintFirst: 'stroke',
+ cursorDelay: 250,
+ strokeDashArray: false,
+ inGroup: false,
+ id: 'Shape' + layer_count,
+ shadow: {
+ color: '#000',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ canvas.add(newrect);
+ newLayer(newrect);
+ canvas.setActiveObject(newrect);
+ canvas.bringToFront(newrect);
+ canvas.renderAll();
+}
+
+// Change text format
+function formatText() {
+ var isselected = false;
+ if (!canvas.getActiveObject().isEditing) {
+ canvas.getActiveObject().enterEditing();
+ canvas.getActiveObject().selectAll();
+ isselected = true;
+ }
+ if ($(this).hasClass('format-text-active')) {
+ if ($(this).attr('id') == 'format-bold') {
+ $(this).find('img').attr('src', 'assets/bold.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ fontWeight: 'normal' });
+ } else if ($(this).attr('id') == 'format-italic') {
+ $(this).find('img').attr('src', 'assets/italic.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ fontStyle: 'normal' });
+ } else if ($(this).attr('id') == 'format-underline') {
+ $(this).find('img').attr('src', 'assets/underline.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ underline: false });
+ } else {
+ $(this).find('img').attr('src', 'assets/strike.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ linethrough: false });
+ }
+ $(this).removeClass('format-text-active');
+ } else {
+ $(this).addClass('format-text-active');
+ if ($(this).attr('id') == 'format-bold') {
+ $(this).find('img').attr('src', 'assets/bold-active.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ fontWeight: 'bold' });
+ } else if ($(this).attr('id') == 'format-italic') {
+ $(this).find('img').attr('src', 'assets/italic-active.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ fontStyle: 'italic' });
+ } else if ($(this).attr('id') == 'format-underline') {
+ $(this).find('img').attr('src', 'assets/underline-active.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ underline: true });
+ } else {
+ $(this).find('img').attr('src', 'assets/strike-active.svg');
+ canvas
+ .getActiveObject()
+ .setSelectionStyles({ linethrough: true });
+ }
+ }
+ if (isselected) {
+ canvas.getActiveObject().exitEditing();
+ }
+ canvas.renderAll();
+ save();
+}
+$(document).on('click', '.format-text', formatText);
+
+// Change stroke type (e.g. dashed), ignore naming (used to be for line join)
+function lineJoin() {
+ if ($('.line-join-active').attr('id') == 'miter') {
+ $('.line-join-active')
+ .find('img')
+ .attr('src', 'assets/miter.svg');
+ } else if ($('.line-join-active').attr('id') == 'bevel') {
+ $('.line-join-active')
+ .find('img')
+ .attr('src', 'assets/bevel.svg');
+ } else if ($('.line-join-active').attr('id') == 'round') {
+ $('.line-join-active')
+ .find('img')
+ .attr('src', 'assets/round.svg');
+ } else if ($('.line-join-active').attr('id') == 'small-dash') {
+ $('.line-join-active')
+ .find('img')
+ .attr('src', 'assets/dash2.svg');
+ }
+ $('.line-join-active').removeClass('line-join-active');
+ $(this).addClass('line-join-active');
+ if ($(this).attr('id') == 'miter') {
+ $(this).find('img').attr('src', 'assets/miter-active.svg');
+ canvas
+ .getActiveObject()
+ .set({ strokeWidth: 0, strokeDashArray: false });
+ canvas.renderAll();
+ updatePanelValues();
+ } else if ($(this).attr('id') == 'bevel') {
+ $(this).find('img').attr('src', 'assets/bevel-active.svg');
+ canvas.getActiveObject().set({ strokeDashArray: false });
+ if (canvas.getActiveObject().get('strokeWidth') == 0) {
+ canvas.getActiveObject().set({ strokeWidth: 1 });
+ canvas.renderAll();
+ updatePanelValues();
+ }
+ } else if ($(this).attr('id') == 'round') {
+ $(this).find('img').attr('src', 'assets/round-active.svg');
+ canvas.getActiveObject().set({ strokeDashArray: [10, 5] });
+ if (canvas.getActiveObject().get('strokeWidth') == 0) {
+ canvas.getActiveObject().set({ strokeWidth: 1 });
+ canvas.renderAll();
+ updatePanelValues();
+ }
+ } else {
+ $(this).find('img').attr('src', 'assets/dash2-active.svg');
+ canvas.getActiveObject().set({ strokeDashArray: [3, 3] });
+ if (canvas.getActiveObject().get('strokeWidth') == 0) {
+ canvas.getActiveObject().set({ strokeWidth: 1 });
+ canvas.renderAll();
+ updatePanelValues();
+ }
+ }
+ canvas.renderAll();
+ save();
+}
+$(document).on('click', '.line-join', lineJoin);
+
+// Change text alignment
+function alignText() {
+ var textalign;
+ if ($('.align-text-active').attr('id') == 'align-text-left') {
+ $('.align-text-active')
+ .find('img')
+ .attr('src', 'assets/align-text-left.svg');
+ } else if (
+ $('.align-text-active').attr('id') == 'align-text-center'
+ ) {
+ $('.align-text-active')
+ .find('img')
+ .attr('src', 'assets/align-text-center.svg');
+ } else if (
+ $('.align-text-active').attr('id') == 'align-text-right'
+ ) {
+ $('.align-text-active')
+ .find('img')
+ .attr('src', 'assets/align-text-right.svg');
+ } else {
+ $('.align-text-active')
+ .find('img')
+ .attr('src', 'assets/align-text-justify.svg');
+ }
+ $('.align-text-active').removeClass('align-text-active');
+ $(this).addClass('align-text-active');
+ if ($(this).attr('id') == 'align-text-left') {
+ textalign = 'left';
+ $(this)
+ .find('img')
+ .attr('src', 'assets/align-text-left-active.svg');
+ } else if ($(this).attr('id') == 'align-text-center') {
+ textalign = 'center';
+ $(this)
+ .find('img')
+ .attr('src', 'assets/align-text-center-active.svg');
+ } else if ($(this).attr('id') == 'align-text-right') {
+ textalign = 'right';
+ $(this)
+ .find('img')
+ .attr('src', 'assets/align-text-right-active.svg');
+ } else {
+ textalign = 'justify';
+ $(this)
+ .find('img')
+ .attr('src', 'assets/align-text-justify-active.svg');
+ }
+ canvas.getActiveObject().set({ textAlign: textalign });
+ canvas.renderAll();
+ save();
+}
+$(document).on('click', '.align-text', alignText);
+
+// Change font
+function changeFont() {
+ var font = $('#font-picker').val();
+ if (canvas.getActiveObject().get('assetType')) {
+ WebFont.load({
+ google: {
+ families: [font],
+ },
+ active: () => {
+ var object = canvas.getActiveObject();
+ animatedtext
+ .find((x) => x.id == object.id)
+ .reset(
+ animatedtext.find((x) => x.id == object.id).text,
+ $.extend(
+ animatedtext.find((x) => x.id == object.id).props,
+ { fontFamily: font }
+ ),
+ canvas
+ );
+ save();
+ },
+ });
+ save();
+ } else {
+ WebFont.load({
+ google: {
+ families: [font],
+ },
+ active: () => {
+ canvas.getActiveObject().set('fontFamily', font);
+ canvas.renderAll();
+ save();
+ },
+ });
+ }
+}
+$(document).on('change', '#font-picker', changeFont);
+
+// Calculate text width to display it in 1 line
+function calculateTextWidth(text, font) {
+ var ctx = canvas.getContext('2d');
+ ctx.font = font;
+ return ctx.measureText(text).width + 10;
+}
+
+// Create an audio layer
+function newAudioLayer(src) {
+ var audio = new Audio(src);
+ audio.crossOrigin = 'anonymous';
+ audio.addEventListener('loadeddata', () => {
+ var nullobject = new fabric.Rect({
+ id: 'Audio' + layer_count,
+ width: 10,
+ height: 10,
+ audioSrc: src,
+ duration: audio.duration * 1000,
+ opacity: 0,
+ selectable: false,
+ volume: 0.5,
+ assetType: 'audio',
+ shadow: {
+ color: '#000',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ canvas.add(nullobject);
+ newLayer(nullobject);
+ });
+}
+
+// Create a textbox
+function newTextbox(
+ fontsize,
+ fontweight,
+ text,
+ x,
+ y,
+ width,
+ center,
+ font
+) {
+ var newtext = new fabric.Textbox(text, {
+ left: x,
+ top: y,
+ originX: 'center',
+ originY: 'center',
+ fontFamily: 'Inter',
+ fill: '#000',
+ fontSize: fontsize,
+ fontWeight: fontweight,
+ textAlign: 'center',
+ cursorWidth: 1,
+ stroke: '#000',
+ strokeWidth: 0,
+ cursorDuration: 1,
+ paintFirst: 'stroke',
+ objectCaching: false,
+ absolutePositioned: true,
+ strokeUniform: true,
+ inGroup: false,
+ cursorDelay: 250,
+ strokeDashArray: false,
+ width: calculateTextWidth(
+ text,
+ fontweight + ' ' + fontsize + 'px Inter'
+ ),
+ id: 'Text' + layer_count,
+ shadow: {
+ color: '#000',
+ offsetX: 0,
+ offsetY: 0,
+ blur: 0,
+ opacity: 0,
+ },
+ });
+ newtext.setControlsVisibility({
+ mt: false,
+ mb: false,
+ });
+ canvas.add(newtext);
+ newLayer(newtext);
+ canvas.setActiveObject(newtext);
+ canvas.bringToFront(newtext);
+ newtext.enterEditing();
+ newtext.selectAll();
+ canvas.renderAll();
+ if (center) {
+ newtext.set(
+ 'left',
+ artboard.get('left') + artboard.get('width') / 2
+ );
+ newtext.set(
+ 'top',
+ artboard.get('top') + artboard.get('height') / 2
+ );
+ canvas.renderAll();
+ }
+ canvas.getActiveObject().set('fontFamily', font);
+ canvas.renderAll();
+}
+
+function deleteObject(object, def = true) {
+ if (object.get('assetType') == 'animatedText' && def) {
+ animatedtext = $.grep(animatedtext, function (a) {
+ return a.id != object.id;
+ });
+ }
+ if (object.type == 'image') {
+ var temp = files.find((x) => x.name == object.get('id'));
+ files = $.grep(files, function (a) {
+ return a != temp.name;
+ });
+ }
+ $(".layer[data-object='" + object.get('id') + "']").remove();
+ $('#' + object.get('id')).remove();
+ keyframes = $.grep(keyframes, function (e) {
+ return e.id != object.get('id');
+ });
+ p_keyframes = $.grep(p_keyframes, function (e) {
+ return e.id != object.get('id');
+ });
+ objects = $.grep(objects, function (e) {
+ return e.id != object.get('id');
+ });
+ canvas.remove(object);
+ canvas.renderAll();
+ canvas.discardActiveObject();
+ save();
+ if (objects.length == 0) {
+ $('#nolayers').removeClass('yaylayers');
+ }
+}
+
+// Delete selected object
+function deleteSelection() {
+ if (
+ canvas.getActiveObject() &&
+ !canvas.getActiveObject().isEditing
+ ) {
+ const selection = canvas.getActiveObject();
+ if (selection.type == 'activeSelection') {
+ canvas.discardActiveObject();
+ selection._objects.forEach(function (object) {
+ deleteObject(object);
+ });
+ } else {
+ deleteObject(canvas.getActiveObject());
+ }
+ }
+}
+
+// Expand / collapse layer
+function toggleLayer() {
+ const layerid = $(this).parent().parent().attr('data-object');
+ $(this).parent().parent().find('.properties').toggle();
+ $(this).parent().parent().find('.droparrow').toggleClass('layeron');
+ $(".keyframe-row[data-object='" + layerid + "']").toggle();
+ setTimelineZoom(timelinetime);
+}
+$(document).on('click', '.droparrow', toggleLayer);
+
+// Select layer
+function selectLayer(e) {
+ if (!$(e.target).hasClass('droparrow')) {
+ const layerid = $(this).parent().attr('data-object');
+ $('.layer-selected').removeClass('layer-selected');
+ $(this).parent().addClass('layer-selected');
+ canvas.setActiveObject(canvas.getItemById(layerid));
+ }
+}
+$(document).on('click', '.layer-name', selectLayer);
+
+// Set video duration
+function setDuration(length) {
+ $('#inner-timeline').css('width', length / timelinetime + 50);
+ $('#inner-seekarea').css('width', length / timelinetime + 50);
+ duration = length;
+ var minutes = Math.floor(duration / 1000 / 60);
+ var seconds = (duration / 1000 - minutes * 60).toFixed(2);
+ $('#total-time input').val(
+ ('0' + minutes).slice(-2) +
+ ':' +
+ ('0' + Math.floor(seconds)).slice(-2) +
+ ':' +
+ ('0' + Math.floor((seconds % 1) * 100)).slice(-2)
+ );
+ $('.object-props').each(function () {
+ $(this).css(
+ 'width',
+ duration / timelinetime -
+ p_keyframes.find((x) => x.id == $(this).attr('id')).start /
+ timelinetime +
+ 'px'
+ );
+ p_keyframes.find((x) => x.id == $(this).attr('id')).end =
+ duration;
+ if (
+ p_keyframes.find((x) => x.id == $(this).attr('id')).trimend >
+ p_keyframes.find((x) => x.id == $(this).attr('id')).end
+ ) {
+ p_keyframes.find((x) => x.id == $(this).attr('id')).trimend =
+ duration;
+ $(this)
+ .find('.trim-row')
+ .css(
+ 'width',
+ duration / timelinetime -
+ p_keyframes.find((x) => x.id == $(this).attr('id'))
+ .trimstart /
+ timelinetime +
+ 'px'
+ );
+ }
+ });
+ setTimelineZoom(timelinetime);
+ save();
+}
+
+// Render time markers
+function renderTimeMarkers() {
+ var renderoffset = 1000 / timelinetime - 20;
+ var timenumber = 0;
+ var modulo = 1;
+ if (timelinetime > 18) {
+ modulo = 5;
+ } else if (timelinetime > 12) {
+ modulo = 2;
+ }
+ $('#time-numbers').html('');
+ $('#time-numbers').append(
+ "" +
+ timenumber +
+ 's
'
+ );
+ timenumber++;
+ while (timenumber * 1000 <= duration) {
+ $('#time-numbers').append(
+ "" +
+ timenumber +
+ 's
'
+ );
+ if (timenumber % modulo != 0) {
+ $('.time-number:last-child()').css('opacity', '0');
+ }
+ timenumber++;
+ }
+}
+
+// Change timeline zoom level
+function setTimelineZoom(time) {
+ $('.object-props').each(function () {
+ $(this).offset({
+ left:
+ p_keyframes.find((x) => x.id == $(this).attr('id')).start /
+ time +
+ $('#inner-timeline').offset().left +
+ offset_left,
+ });
+ $(this).css({ width: ($(this).width() * timelinetime) / time });
+ $(this)
+ .find('.trim-row')
+ .css({
+ left:
+ p_keyframes.find((x) => x.id == $(this).attr('id'))
+ .trimstart / time,
+ });
+ $(this)
+ .find('.trim-row')
+ .css({
+ width:
+ ($(this).find('.trim-row').width() * timelinetime) / time,
+ });
+ });
+ timelinetime = time;
+ $('.keyframe').each(function () {
+ $(this).offset({
+ left:
+ $(this).attr('data-time') / timelinetime +
+ $('#inner-timeline').offset().left +
+ offset_left,
+ });
+ });
+ $('#seekbar').offset({
+ left:
+ $('#inner-timeline').offset().left +
+ currenttime / timelinetime +
+ offset_left,
+ });
+ $('#inner-timeline').css({ width: duration / timelinetime + 50 });
+ $('#inner-seekarea').css({
+ minWidth: duration / timelinetime + 50,
+ });
+ renderTimeMarkers();
+}
+$(document).on('input', '#timeline-zoom', function () {
+ setTimelineZoom($('#timeline-zoom').val());
+});
+
+function removeKeyframe() {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != selectedkeyframe.attr('data-property')
+ );
+ });
+ if (selectedkeyframe.attr('data-property') == 'left') {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'top'
+ );
+ });
+ } else if (selectedkeyframe.attr('data-property') == 'scaleX') {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'scaleY'
+ );
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'width'
+ );
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'height'
+ );
+ });
+ } else if (
+ selectedkeyframe.attr('data-property') == 'strokeWidth'
+ ) {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'stroke'
+ );
+ });
+ } else if (
+ selectedkeyframe.attr('data-property') == 'shadow.color'
+ ) {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'shadow.blur'
+ );
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'shadow.offsetX'
+ );
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'shadow.offsetY'
+ );
+ });
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'shadow.opacity'
+ );
+ });
+ } else if (
+ selectedkeyframe.attr('data-property') == 'charSpacing'
+ ) {
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != selectedkeyframe.attr('data-time') ||
+ e.id != selectedkeyframe.attr('data-object') ||
+ e.name != 'lineHeight'
+ );
+ });
+ }
+ selectedkeyframe.remove();
+ $('#keyframe-properties').removeClass('show-properties');
+}
+
+// Delete a keyframe
+function deleteKeyframe() {
+ if (shiftkeys.length > 0) {
+ shiftkeys.forEach(function (key) {
+ selectedkeyframe = $(key.keyframe);
+ removeKeyframe();
+ });
+ shiftkeys = [];
+ } else {
+ removeKeyframe();
+ }
+ animate(false, currenttime);
+ save();
+}
+$(document).on('click', '#delete-keyframe', deleteKeyframe);
+
+// Copy keyframes
+function copyKeyframes() {
+ clipboard.sort(function (a, b) {
+ return a.t - b.t;
+ });
+ var inittime = clipboard[0].t;
+ clipboard.forEach(function (keyframe) {
+ var newtime = keyframe.t - inittime + currenttime;
+ newKeyframe(
+ keyframe.name,
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyframe.value,
+ true
+ );
+ var keyprop = keyframe.name;
+ if (keyprop == 'left') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t && e.id == keyframe.id && e.name == 'top'
+ );
+ });
+ newKeyframe(
+ 'top',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ } else if (keyprop == 'scaleX') {
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'scaleY'
+ );
+ });
+ newKeyframe(
+ 'scaleY',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'width'
+ );
+ });
+ if (keyarr2.length > 0) {
+ newKeyframe(
+ 'width',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ }
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'height'
+ );
+ });
+ if (keyarr2.length > 0) {
+ newKeyframe(
+ 'height',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ }
+ } else if (keyprop == 'strokeWidth') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'stroke'
+ );
+ });
+ newKeyframe(
+ 'stroke',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ } else if (keyprop == 'charSpacing') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'lineHeight'
+ );
+ });
+ newKeyframe(
+ 'lineHeight',
+ canvas.getItemByid(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ } else if (keyprop == 'shadow.color') {
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'shadow.opacity'
+ );
+ });
+ newKeyframe(
+ 'shadow.opacity',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'shadow.offsetX'
+ );
+ });
+ newKeyframe(
+ 'shadow.offsetX',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'shadow.offsetY'
+ );
+ });
+
+ newKeyframe(
+ 'shadow.offsetY',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == keyframe.t &&
+ e.id == keyframe.id &&
+ e.name == 'shadow.blur'
+ );
+ });
+ newKeyframe(
+ 'shadow.blur',
+ canvas.getItemById(keyframe.id),
+ newtime,
+ keyarr2[0].value,
+ true
+ );
+ }
+ save();
+ });
+}
+
+// Update keyframe (after dragging)
+function updateKeyframe(drag, newval, offset) {
+ var time = parseFloat(
+ (drag.position().left * timelinetime).toFixed(1)
+ );
+ const keyprop = drag.attr('data-property');
+ const keytime = drag.attr('data-time');
+ const keyarr = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == keyprop
+ );
+ });
+ const keyobj = canvas.getItemById(keyarr[0].id);
+ time =
+ parseFloat(
+ p_keyframes.find((x) => x.id == keyobj.get('id')).start
+ ) + time;
+ if (newval) {
+ time = currenttime;
+ }
+ var keyval = keyarr[0].value;
+ if (newval) {
+ if (keyprop == 'shadow.color') {
+ keyval = keyobj.shadow.color;
+ } else if (keyprop == 'volume') {
+ keyval = parseFloat($('#object-volume input').val() / 200);
+ } else {
+ keyval = keyobj.get(keyprop);
+ }
+ } else if (keyprop == 'left') {
+ keyval = keyval + artboard.get('left');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != keyprop
+ );
+ });
+ newKeyframe(keyprop, keyobj, time, keyval, false);
+ if (keyprop == 'left') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'top'
+ );
+ });
+ var keyval2 = keyarr2[0].value + artboard.get('top');
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('top');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'top'
+ );
+ });
+ newKeyframe('top', keyobj, time, keyval2, false);
+ } else if (keyprop == 'scaleX') {
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'scaleY'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('scaleY');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'scaleY'
+ );
+ });
+ newKeyframe('scaleY', keyobj, time, keyval2, false);
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'width'
+ );
+ });
+ if (keyarr2.length > 0) {
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('width');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'width'
+ );
+ });
+ newKeyframe('width', keyobj, time, keyval2, false);
+ }
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'height'
+ );
+ });
+ if (keyarr2.length > 0) {
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('height');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'height'
+ );
+ });
+ newKeyframe('height', keyobj, time, keyval2, false);
+ }
+ } else if (keyprop == 'strokeWidth') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'stroke'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('stroke');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'stroke'
+ );
+ });
+ newKeyframe('stroke', keyobj, time, keyval2, false);
+ } else if (keyprop == 'charSpacing') {
+ const keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'lineHeight'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).get('lineHeight');
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'lineHeight'
+ );
+ });
+ newKeyframe('lineHeight', keyobj, time, keyval2, false);
+ } else if (keyprop == 'shadow.color') {
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'shadow.opacity'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).shadow.opacity;
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'shadow.opacity'
+ );
+ });
+ newKeyframe('shadow.opacity', keyobj, time, keyval2, false);
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'shadow.offsetX'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).shadow.offsetX;
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'shadow.offsetX'
+ );
+ });
+ newKeyframe('shadow.offsetX', keyobj, time, keyval2, false);
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'shadow.offsetY'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).shadow.offsetY;
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'shadow.offsetY'
+ );
+ });
+ newKeyframe('shadow.offsetY', keyobj, time, keyval2, false);
+ var keyarr2 = $.grep(keyframes, function (e) {
+ return (
+ e.t == parseFloat(keytime) &&
+ e.id == drag.attr('data-object') &&
+ e.name == 'shadow.blur'
+ );
+ });
+ var keyval2 = keyarr2[0].value;
+ if (newval) {
+ keyval2 = canvas.getItemById(keyarr2[0].id).shadow.blur;
+ }
+ keyframes = $.grep(keyframes, function (e) {
+ return (
+ e.t != parseFloat(keytime) ||
+ e.id != drag.attr('data-object') ||
+ e.name != 'shadow.blur'
+ );
+ });
+ newKeyframe('shadow.blur', keyobj, time, keyval2, false);
+ }
+ if (offset) {
+ drag.attr('data-time', time);
+ } else {
+ drag.attr(
+ 'data-time',
+ time + p_keyframes.find((x) => x.id == keyarr[0].id).start
+ );
+ }
+ keyframes.sort(function (a, b) {
+ if (a.id.indexOf('Group') >= 0 && b.id.indexOf('Group') == -1) {
+ return 1;
+ } else if (
+ b.id.indexOf('Group') >= 0 &&
+ a.id.indexOf('Group') == -1
+ ) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+}
+
+function keyframeSnap(drag) {
+ if (shiftkeys.length == 0) {
+ if (
+ drag.offset().left > $('#seekbar').offset().left - 5 &&
+ drag.offset().left < $('#seekbar').offset().left + 5
+ ) {
+ drag.offset({ left: $('#seekbar').offset().left });
+ $('#line-snap').offset({
+ left: $('#seekbar').offset().left,
+ top: drag.parent().parent().offset().top,
+ });
+ $('#line-snap').css({
+ height: drag.parent().parent().height(),
+ });
+ $('#line-snap').addClass('line-active');
+ } else {
+ drag
+ .parent()
+ .parent()
+ .find('.keyframe')
+ .each(function (index) {
+ if (!drag.is($(this))) {
+ if (
+ drag.offset().left > $(this).offset().left - 5 &&
+ drag.offset().left < $(this).offset().left + 5
+ ) {
+ drag.offset({ left: $(this).offset().left });
+ $('#line-snap').offset({
+ left: $(this).offset().left,
+ top: drag.parent().parent().offset().top,
+ });
+ $('#line-snap').css({
+ height: drag.parent().parent().height(),
+ });
+ $('#line-snap').addClass('line-active');
+ return false;
+ }
+ }
+ if (index == $('.keyframe').length - 1) {
+ $('#line-snap').removeClass('line-active');
+ }
+ });
+ }
+ }
+}
+
+// Dragging a keyframe
+function dragKeyframe(e) {
+ if (e.which == 3) {
+ return false;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ var inst = this;
+ var drag = $(this);
+ var pageX = e.pageX;
+ var offset = $(this).offset();
+ var move = false;
+ if (e.shiftKey) {
+ if (!$(this).hasClass('keyframe-selected')) {
+ shiftkeys.push({
+ keyframe: this,
+ offset: $(this).offset().left,
+ });
+ $(this).addClass('keyframe-selected');
+ } else {
+ shiftkeys = $.grep(shiftkeys, function (e) {
+ return e.keyframe != this;
+ });
+ $(this).removeClass('keyframe-selected');
+ }
+ }
+ if (shiftkeys.length > 0) {
+ shiftkeys.forEach(function (key) {
+ key.offset = $(key.keyframe).offset().left;
+ });
+ }
+ function draggingKeyframe(e) {
+ move = true;
+ var left = offset.left + (e.pageX - pageX);
+ if (shiftkeys.length == 0) {
+ if (left > $('#timearea').offset().left + offset_left) {
+ drag.offset({ left: left });
+ } else {
+ drag.offset({
+ left: $('#timearea').offset().left + offset_left,
+ });
+ }
+ keyframeSnap(drag);
+ } else {
+ shiftkeys.forEach(function (key) {
+ if (key.keyframe != inst) {
+ $(key.keyframe).offset({
+ left: key.offset + (e.pageX - pageX),
+ });
+ keyframeSnap($(key.keyframe));
+ } else {
+ drag.offset({ left: left });
+ keyframeSnap(drag);
+ }
+ });
+ }
+ }
+ function releasedKeyframe(e) {
+ $('body')
+ .off('mousemove', draggingKeyframe)
+ .off('mouseup', releasedKeyframe);
+ $('#line-snap').removeClass('line-active');
+ if (move) {
+ if (shiftkeys.length == 0) {
+ // Check for 60FPS playback, 16ms "slots"
+ var time = parseFloat(
+ (drag.position().left * timelinetime).toFixed(1)
+ );
+ if (time % 16.666 != 0) {
+ drag.offset({
+ left:
+ (Math.ceil(time / 16.666) * 16.666) / timelinetime +
+ drag.parent().offset().left,
+ });
+ updateKeyframe(drag, false);
+ } else {
+ updateKeyframe(drag, false);
+ }
+ } else {
+ shiftkeys.forEach(function (key) {
+ // Check for 60FPS playback, 16ms "slots"
+ var time = parseFloat(
+ ($(key.keyframe).position().left * timelinetime).toFixed(
+ 1
+ )
+ );
+ if (time % 16.666 != 0) {
+ $(key.keyframe).offset({
+ left:
+ (Math.ceil(time / 16.666) * 16.666) / timelinetime +
+ $(key.keyframe).parent().offset().left,
+ });
+ updateKeyframe($(key.keyframe), false);
+ } else {
+ updateKeyframe($(key.keyframe), false);
+ }
+ });
+ }
+ } else if (!e.shiftDown) {
+ keyframeProperties(inst);
+ }
+ move = false;
+ $('.line-active').removeClass('line-active');
+ save();
+ }
+ $('body')
+ .on('mouseup', releasedKeyframe)
+ .on('mousemove', draggingKeyframe);
+}
+$(document).on('mousedown', '.keyframe', dragKeyframe);
+
+// Render current time in the playback area
+function renderTime() {
+ var minutes = Math.floor(currenttime / 1000 / 60);
+ var seconds = (currenttime / 1000 - minutes * 60).toFixed(2);
+ $('#current-time input').val(
+ ('0' + minutes).slice(-2) +
+ ':' +
+ ('0' + Math.floor(seconds)).slice(-2) +
+ ':' +
+ ('0' + Math.floor((seconds % 1) * 100)).slice(-2)
+ );
+}
+
+// Update current time (and account for frame "slots")
+function updateTime(drag, check) {
+ if ($('#timeline').scrollLeft() > offset_left) {
+ currenttime = parseFloat(
+ (
+ (drag.position().left +
+ $('#timeline').scrollLeft() -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ } else {
+ currenttime = parseFloat(
+ (
+ (drag.position().left +
+ $('#timeline').scrollLeft() -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ }
+ // Check for 60FPS playback, 16ms "slots"
+ if (currenttime % 16.666 != 0 && !check) {
+ currenttime = Math.ceil(currenttime / 16.666) * 16.666;
+ }
+ renderTime();
+ pause();
+ animate(false, currenttime);
+}
+
+// Dragging the seekbar
+function dragSeekBar(e) {
+ if (e.which == 3) {
+ return false;
+ }
+ var drag = $(this);
+ var pageX = e.pageX;
+ var offset = $(this).offset();
+ tempselection = canvas.getActiveObject();
+ canvas.discardActiveObject();
+ function dragging(e) {
+ paused = true;
+ var left = offset.left + (e.pageX - pageX);
+ if (
+ left > $('#timearea').offset().left + offset_left &&
+ left - $('#timearea').offset().left <
+ duration / timelinetime + offset_left
+ ) {
+ drag.offset({ left: left });
+ } else if (left < $('#timearea').offset().left + offset_left) {
+ drag.offset({
+ left: offset_left + $('#timearea').offset().left,
+ });
+ }
+ if ($('#timeline').scrollLeft() > offset_left) {
+ currenttime = parseFloat(
+ (
+ (drag.position().left +
+ $('#timeline').scrollLeft() -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ } else {
+ currenttime = parseFloat(
+ (
+ (drag.position().left +
+ $('#timeline').scrollLeft() -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ }
+ animate(false, currenttime);
+ seeking = true;
+ renderTime();
+ }
+ function released(e) {
+ $('body').off('mousemove', dragging).off('mouseup', released);
+ updateTime(drag, false);
+ seeking = false;
+ if (tempselection && tempselection.type != 'activeSelection') {
+ reselect(tempselection);
+ }
+ updatePanelValues();
+ }
+ $('body').on('mouseup', released).on('mousemove', dragging);
+}
+$(document).on('mousedown', '#seekbar', dragSeekBar);
+
+// Dragging layer horizontally
+function dragObjectProps(e) {
+ if (e.which == 3) {
+ return false;
+ }
+ var drag = $(this).parent();
+ var drag2 = $(this).find('.trim-row');
+ var target = e.target;
+ var pageX = e.pageX;
+ var offset = drag.offset();
+ var offset2 = drag2.offset();
+ var initwidth = drag2.width();
+ var initpos = drag2.position().left;
+ var opened = false;
+ var trim = 'no';
+ // Trim layer to hovered area
+ if (e.metaKey) {
+ if (e.shiftKey) {
+ if (drag2.position().left + e.pageX >= 0) {
+ drag2.offset({
+ left:
+ hovertime / timelinetime -
+ p_keyframes.find((x) => x.id == drag.attr('id'))
+ .trimstart /
+ timelinetime +
+ offset2.left,
+ });
+ const leftval = parseFloat(
+ (drag2.position().left * timelinetime).toFixed(1)
+ );
+ p_keyframes.find((x) => x.id == drag.attr('id')).trimstart =
+ leftval;
+ drag2.css({
+ width:
+ (p_keyframes.find((x) => x.id == drag.attr('id'))
+ .trimend -
+ p_keyframes.find((x) => x.id == drag.attr('id'))
+ .trimstart) /
+ timelinetime,
+ });
+ return false;
+ }
+ } else {
+ if (
+ hovertime +
+ p_keyframes.find((x) => x.id == drag.attr('id')).start <
+ duration
+ ) {
+ drag2.css({
+ width:
+ hovertime / timelinetime -
+ p_keyframes.find((x) => x.id == drag.attr('id'))
+ .trimstart /
+ timelinetime,
+ });
+ save();
+ p_keyframes.find((x) => x.id == drag.attr('id')).end =
+ hovertime;
+ p_keyframes.find((x) => x.id == drag.attr('id')).trimend =
+ hovertime;
+ }
+ return false;
+ }
+ }
+ if (pageX - $(this).find('.trim-row').offset().left < 7) {
+ trim = 'left';
+ } else if (
+ pageX - $(this).find('.trim-row').offset().left >
+ $(this).find('.trim-row').width() - 7
+ ) {
+ trim = 'right';
+ }
+ function dragging(e) {
+ if (trim == 'no') {
+ var left = offset.left + (e.pageX - pageX);
+ if (
+ left >
+ $('#timearea').offset().left +
+ offset_left -
+ $('#timeline').scrollLeft()
+ ) {
+ drag.offset({ left: left });
+ } else if (
+ left + $('#timeline').scrollLeft() <
+ $('#timearea').offset().left + offset_left
+ ) {
+ drag.css({ left: offset_left });
+ }
+ p_keyframes.find((x) => x.id == drag.attr('id')).start =
+ parseFloat(
+ (
+ (drag.position().left -
+ offset_left +
+ $('#timeline').scrollLeft()) *
+ timelinetime
+ ).toFixed(1)
+ );
+ p_keyframes.find((x) => x.id == drag.attr('id')).end =
+ parseFloat(
+ (
+ (drag.position().left +
+ drag.width() -
+ offset_left +
+ $('#timeline').scrollLeft()) *
+ timelinetime
+ ).toFixed(1)
+ );
+ if (
+ $(".keyframe-row[data-object='" + drag.attr('id') + "']").is(
+ ':hidden'
+ )
+ ) {
+ opened = true;
+ $(".layer[data-object='" + drag.attr('id') + "']")
+ .find('.properties')
+ .toggle();
+ $(".layer[data-object='" + drag.attr('id') + "']")
+ .find('.properties')
+ .toggleClass('layeron');
+ $(
+ ".keyframe-row[data-object='" + drag.attr('id') + "']"
+ ).toggle();
+ setTimelineZoom(timelinetime);
+ }
+ drag.find('.keyframe').each(function () {
+ updateKeyframe($(this), false, true);
+ });
+ animate(false, currenttime);
+ } else if (trim == 'left') {
+ if (drag2.position().left + (e.pageX - pageX) >= 0) {
+ drag2.offset({
+ left: offset2.left + (e.pageX - pageX),
+ });
+ drag2.css({
+ width: initwidth - (-initpos + drag2.position().left),
+ });
+ const leftval = parseFloat(
+ (drag2.position().left * timelinetime).toFixed(1)
+ );
+ p_keyframes.find((x) => x.id == drag.attr('id')).trimstart =
+ leftval;
+ }
+ } else if (trim == 'right') {
+ if (initwidth + (e.pageX - pageX) < duration / timelinetime) {
+ drag2.css({
+ width: initwidth + (e.pageX - pageX),
+ });
+ } else {
+ drag2.css({
+ width:
+ duration / timelinetime -
+ drag.position().left -
+ $('#timeline').scrollLeft() +
+ offset_left,
+ });
+ }
+ const rightval = parseFloat(
+ (
+ (drag2.position().left + drag2.width()) *
+ timelinetime
+ ).toFixed(1)
+ );
+ p_keyframes.find((x) => x.id == drag.attr('id')).end = rightval;
+ p_keyframes.find((x) => x.id == drag.attr('id')).trimend =
+ rightval;
+ }
+ }
+ function released(e) {
+ $('body').off('mousemove', dragging).off('mouseup', released);
+ if (opened) {
+ $(".layer[data-object='" + drag.attr('id') + "']")
+ .find('.properties')
+ .toggle();
+ $(".layer[data-object='" + drag.attr('id') + "']")
+ .find('.properties')
+ .toggleClass('layeron');
+ $(
+ ".keyframe-row[data-object='" + drag.attr('id') + "']"
+ ).toggle();
+ setTimelineZoom(timelinetime);
+ }
+ animate(false, currenttime);
+ save();
+ }
+ $('body').on('mouseup', released).on('mousemove', dragging);
+}
+$(document).on('mousedown', '.main-row', dragObjectProps);
+
+function resetHeight() {
+ var top = $(window).height() - oldtimelinepos - 92;
+ if ($('#upload-tool').hasClass('tool-active')) {
+ $('#browser').css('top', '150px');
+ $('#browser').css(
+ 'height',
+ 'calc(100% - ' + (top + 97 + 150) + 'px)'
+ );
+ } else {
+ $('#browser').css('top', '110px');
+ $('#browser').css(
+ 'height',
+ 'calc(100% - ' + (top + 97 + 100) + 'px)'
+ );
+ }
+ $('#timearea').css('height', top);
+ $('#layer-list').css('height', top);
+ $('#toolbar').css('height', 'calc(100% - ' + (top + 97) + 'px)');
+ $('#canvas-area').css(
+ 'height',
+ 'calc(100% - ' + (top + 97) + 'px)'
+ );
+ $('#properties').css('height', 'calc(100% - ' + (top + 97) + 'px)');
+ $('#timeline-handle').css('bottom', top + 95);
+ resizeCanvas();
+}
+
+// Dragging timeline vertically
+function dragTimeline(e) {
+ const disableselect = (e) => {
+ return false
+ }
+ document.onselectstart = disableselect
+ document.onmousedown = disableselect
+
+ oldtimelinepos = e.pageY;
+ if (e.which == 3) {
+ return false;
+ }
+ function draggingKeyframe(e) {
+ oldtimelinepos = e.pageY;
+ resetHeight(e);
+ }
+ function releasedKeyframe(e) {
+ $('body')
+ .off('mousemove', draggingKeyframe)
+ .off('mouseup', releasedKeyframe);
+ }
+ $('body')
+ .on('mouseup', releasedKeyframe)
+ .on('mousemove', draggingKeyframe);
+}
+
+$(document).on('mousedown', '#timeline-handle', dragTimeline);
+
+oldtimelinepos = $(window).height() - 92 - $('#timearea').height();
+
+// Sync scrolling (vertical)
+function syncScroll(el1, el2) {
+ var $el1 = $(el1);
+ var $el2 = $(el2);
+ var forcedScroll = false;
+ $el1.scroll(function () {
+ performScroll($el1, $el2);
+ });
+ $el2.scroll(function () {
+ performScroll($el2, $el1);
+ });
+
+ function performScroll($scrolled, $toScroll) {
+ if (forcedScroll) return (forcedScroll = false);
+ var percent =
+ ($scrolled.scrollTop() /
+ ($scrolled[0].scrollHeight - $scrolled.outerHeight())) *
+ 100;
+ setScrollTopFromPercent($toScroll, percent);
+ }
+
+ function setScrollTopFromPercent($el, percent) {
+ var scrollTopPos =
+ (percent / 100) * ($el[0].scrollHeight - $el.outerHeight());
+ forcedScroll = true;
+ $el.scrollTop(scrollTopPos);
+ }
+}
+
+// Sync scrolling (horizontal)
+function syncScrollHoz(el1, el2) {
+ var $el1 = $(el1);
+ var $el2 = $(el2);
+ var forcedScroll = false;
+ $el1.scroll(function () {
+ performScroll($el1, $el2);
+ });
+ $el2.scroll(function () {
+ performScroll($el2, $el1);
+ });
+
+ function performScroll($scrolled, $toScroll) {
+ if (forcedScroll) return (forcedScroll = false);
+ var percent =
+ ($scrolled.scrollLeft() / $scrolled.outerWidth()) * 100;
+ setScrollLeftFromPercent($toScroll, percent);
+ }
+
+ function setScrollLeftFromPercent($el, percent) {
+ var scrollLeftPos = (percent / 100) * $el.outerWidth();
+ forcedScroll = true;
+ $el.scrollLeft(scrollLeftPos);
+ }
+}
+
+// Show keyframe properties
+function keyframeProperties(inst) {
+ if (!shiftdown) {
+ selectedkeyframe = $(inst);
+ const popup = $('#keyframe-properties');
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == selectedkeyframe.attr('data-property')
+ );
+ });
+ $('#easing select').val(keyarr[0].easing);
+ $('#easing select').niceSelect('update');
+ popup.css({
+ left: $(inst).offset().left - popup.width() / 2,
+ top: $(inst).offset().top - popup.height() - 20,
+ });
+ popup.addClass('show-properties');
+ $(inst).addClass('keyframe-selected');
+ }
+}
+
+// Apply easing to keyframe
+function applyEasing() {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == selectedkeyframe.attr('data-property')
+ );
+ });
+ keyarr[0].easing = $(this).attr('data-value');
+ if (selectedkeyframe.attr('data-property') == 'left') {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'top'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ } else if (selectedkeyframe.attr('data-property') == 'scaleX') {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'scaleY'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'width'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'height'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ } else if (
+ selectedkeyframe.attr('data-property') == 'strokeWidth'
+ ) {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'stroke'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ } else if (
+ selectedkeyframe.attr('data-property') == 'shadow.color'
+ ) {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'shadow.opacity'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'shadow.offsetX'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'shadow.offsetY'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'shadow.blur'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ } else if (
+ selectedkeyframe.attr('data-property') == 'charSpacing'
+ ) {
+ var keyarr = keyframes.filter(function (e) {
+ return (
+ e.t == selectedkeyframe.attr('data-time') &&
+ e.id == selectedkeyframe.attr('data-object') &&
+ e.name == 'lineHeight'
+ );
+ });
+ keyarr[0].easing = $('#easing select').val();
+ }
+ $('#keyframe-properties').removeClass('show-properties');
+ selectedkeyframe.removeClass('keyframe-selected');
+ save();
+}
+$(document).on('mouseup', '#easing li', applyEasing);
+
+// Click on seek area to seek (still not working properly)
+function seekTo(e) {
+ if ($(e.target).hasClass('keyframe')) {
+ return false;
+ }
+ paused = true;
+ if ($('#seekarea').scrollLeft() > offset_left) {
+ currenttime = parseFloat(
+ (
+ (e.pageX +
+ $('#seekarea').scrollLeft() -
+ $('#timearea').offset().left -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ } else {
+ currenttime = parseFloat(
+ (
+ (e.pageX +
+ $('#seekarea').scrollLeft() -
+ $('#timearea').offset().left -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ }
+ if (currenttime < 0) {
+ currenttime = 0;
+ }
+ // Check for 60FPS playback, 16ms "slots"
+ if (currenttime % 16.666 != 0) {
+ currenttime = Math.ceil(currenttime / 16.666) * 16.666;
+ }
+ renderTime();
+ $('#seekbar').offset({
+ left:
+ offset_left +
+ $('#inner-timeline').offset().left +
+ currenttime / timelinetime,
+ });
+ animate(false, currenttime);
+ updatePanelValues();
+}
+$(document).on('click', '#seekevents', seekTo);
+$(document).on('click', '#timearea', seekTo);
+
+function hideSeekbar() {
+ $('#seek-hover').css({ opacity: 0 });
+}
+function followCursor(e) {
+ $('#seek-hover').css({ opacity: 0.3 });
+ if ($('#seekarea').scrollLeft() > offset_left) {
+ hovertime = parseFloat(
+ (
+ (e.pageX +
+ $('#seekarea').scrollLeft() -
+ $('#timearea').offset().left -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ } else {
+ hovertime = parseFloat(
+ (
+ (e.pageX +
+ $('#seekarea').scrollLeft() -
+ $('#timearea').offset().left -
+ offset_left) *
+ timelinetime
+ ).toFixed(1)
+ );
+ }
+ if (e.pageX >= offset_left + $('#inner-timeline').offset().left) {
+ $('#seek-hover').offset({ left: e.pageX });
+ }
+}
+$(document).on('mousemove', '#timearea', followCursor);
+$(document).on('mousemove', '#seekevents', followCursor);
+$(document).on('mousemove', '#toolbar', hideSeekbar);
+$(document).on('mousemove', '#canvas-area', hideSeekbar);
+$(document).on('mousemove', '#browser', hideSeekbar);
+$(document).on('mousemove', '#properties', hideSeekbar);
+$(document).on('mousemove', '#controls', hideSeekbar);
+
+function orderLayers() {
+ $('.layer').each(function (index) {
+ const object = canvas.getItemById($(this).attr('data-object'));
+ canvas.sendToBack(object);
+ canvas.renderAll();
+ objects.splice(
+ $('.layer').length - index - 1,
+ 0,
+ objects.splice(
+ objects.findIndex((x) => x.id == object.get('id')),
+ 1
+ )[0]
+ );
+ });
+ save();
+}
+
+function handTool() {
+ if ($(this).hasClass('hand-active')) {
+ $(this).removeClass('hand-active');
+ $(this).find('img').attr('src', 'assets/hand-tool.svg');
+ handtool = false;
+ canvas.defaultCursor = 'default';
+ canvas.renderAll();
+ } else {
+ $(this).addClass('hand-active');
+ $(this).find('img').attr('src', 'assets/hand-tool-active.svg');
+ handtool = true;
+ canvas.defaultCursor = 'grab';
+ canvas.renderAll();
+ }
+}
+$(document).on('click', '#hand-tool', handTool);
+// Set defaults
+setDuration(10000);
+checkDB();