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();