Last active
September 9, 2021 12:15
-
-
Save eeropic/e00f0b500d6fc9b2f8d622d4b61b03dc to your computer and use it in GitHub Desktop.
pAEper.jsx & pAEperPlayer.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "lib/json2.js" | |
| //export AE shape layer animations to Paper.js project JSON | |
| //AE matchname shortcuts | |
| ae={ | |
| contents:"ADBE Root Vectors Group", | |
| transform:"ADBE Transform Group", | |
| zerogroup:"ADBE Vectors Group", | |
| group:"ADBE Vector Group", | |
| groupTrans:"ADBE Vector Transform Group", | |
| groupPos:"ADBE Vector Position", | |
| groupAnchor:"ADBE Vector Anchor", | |
| pathGroup:"ADBE Vector Shape - Group", | |
| path:"ADBE Vector Shape", | |
| rect:"ADBE Vector Shape - Rect", | |
| rectSize:"ADBE Vector Rect Size", | |
| rectRound:"ADBE Vector Rect Roundness", | |
| rectPos:"ADBE Vector Rect Position", | |
| ellipse:"ADBE Vector Shape - Ellipse", | |
| ellipseSize:"ADBE Vector Ellipse Size", | |
| ellipsePos:"ADBE Vector Ellipse Position", | |
| fill:"ADBE Vector Graphic - Fill", | |
| fillColor:"ADBE Vector Fill Color", | |
| fillOpacity:"ADBE Vector Fill Opacity", | |
| fillRule:"ADBE Vector Fill Rule", | |
| composite:"ADBE Vector Composite Order", | |
| stroke:"ADBE Vector Graphic - Stroke", | |
| strokeColor:"ADBE Vector Stroke Color", | |
| strokeCap:"ADBE Vector Stroke Line Cap", | |
| strokeJoin:"ADBE Vector Stroke Line Join", | |
| strokeMiter:"ADBE Vector Stroke Miter Limit", | |
| strokeWidth:"ADBE Vector Stroke Width", | |
| strokeOpacity:"ADBE Vector Stroke Opacity", | |
| strokeDashes:"ADBE Vector Stroke Dashes", | |
| //Dash n Gap n | |
| strokeDash:"ADBE Vector Stroke Dash ", | |
| strokeGap:"ADBE Vector Stroke Gap ", | |
| strokeOffset:"ADBE Vector Stroke Offset", | |
| strokeCaps:["","butt","round","square"], | |
| strokeJoins:["", "miter","round","bevel"], | |
| gradFill:"ADBE Vector Graphic - G-Fill", | |
| gradType:"ADBE Vector Grad Type", | |
| gradStart:"ADBE Vector Grad Start Pt", | |
| gradEnd:"ADBE Vector Grad End Pt", | |
| gradColors:"ADBE Vector Grad Colors", | |
| gradStroke:"ADBE Vector Graphic - G-Stroke", | |
| } | |
| //comp=app.project.activeItem | |
| //idx 1-> group 1 root obj | |
| //comp.layers[1](ae.contents)(1)(ae.zerogroup)(ae.stroke)) | |
| level=0; | |
| function getVectorPaths(prop){ | |
| var paths=[] | |
| for(var i=1;i<=prop.numProperties;i++){ | |
| var child=prop.property(i) | |
| if(child.matchName==ae.pathGroup){ | |
| paths.push(child) | |
| } | |
| } | |
| return paths | |
| } | |
| function dumpProps(item,lvl, obj){ | |
| lvl+=1 | |
| for(var i=1;i<=item.numProperties;i++){ | |
| var itemChild=item.property(i) | |
| if(itemChild.matchName==ae.contents){ | |
| dumpProps(itemChild,lvl,obj) | |
| } | |
| if(itemChild.matchName==ae.zerogroup){ | |
| dumpProps(itemChild,lvl,obj) | |
| } | |
| if(itemChild.matchName==ae.group){ | |
| var groupData=["Group",{ | |
| applyMatrix:false, | |
| //position:itemChild(ae.groupTrans)(ae.groupPos).value, | |
| //pivot:itemChild(ae.groupTrans)(ae.groupAnchor).value, | |
| children:[] | |
| }]; | |
| obj[1].children.push(groupData) | |
| dumpProps(itemChild,lvl,groupData) | |
| } | |
| //PATH ANIMATION | |
| if(itemChild.matchName==ae.pathGroup){ | |
| var pathValue=itemChild(ae.path).value; | |
| var pathObject=["Path",{ | |
| applyMatrix:true, | |
| segments:pathValue.vertices.map( | |
| function(x, idx){ | |
| return [x, pathValue.inTangents[idx], pathValue.outTangents[idx]] | |
| }), | |
| closed:pathValue.closed, | |
| data:{animData:{}} | |
| }]; | |
| if(itemChild(ae.path).numKeys>0){ | |
| pathObject[1].data.animData.segments=[] | |
| for(var k=0;k<itemChild(ae.path).numKeys;k++){ | |
| var keyPathValue=itemChild(ae.path).keyValue(k+1) | |
| var keyTime=itemChild(ae.path).keyTime(k+1) | |
| //prop.keyInTemporalEase(1)[0] | |
| var easeInX=1-(itemChild(ae.path).keyInTemporalEase(k+1)[0].influence/100) | |
| var easeInY=1-(itemChild(ae.path).keyInTemporalEase(k+1)[0].influence*itemChild(ae.path).keyInTemporalEase(k+1)[0].speed/100) | |
| var easeOutX=itemChild(ae.path).keyOutTemporalEase(k+1)[0].influence/100 | |
| var easeOutY=itemChild(ae.path).keyOutTemporalEase(k+1)[0].influence*itemChild(ae.path).keyOutTemporalEase(k+1)[0].speed/100 | |
| var segments=keyPathValue.vertices.map( | |
| function(x, idx){ | |
| return [x, keyPathValue.inTangents[idx], keyPathValue.outTangents[idx]] | |
| }) | |
| pathObject[1].data.animData.segments.push({time:keyTime,value:segments, easeIn:[easeInX,easeInY], easeOut:[easeOutX,easeOutY]}) | |
| } | |
| } | |
| try{ | |
| var stroke=itemChild.propertyGroup(1)(ae.stroke); | |
| if(typeof stroke!="undefined"){ | |
| pathObject[1].strokeWidth=stroke(ae.strokeWidth).value | |
| pathObject[1].strokeJoin=ae.strokeJoins[stroke(ae.strokeJoin).value]; | |
| pathObject[1].strokeCap=ae.strokeCaps[stroke(ae.strokeCap).value]; | |
| pathObject[1].strokeColor=stroke(ae.strokeColor).value | |
| if(stroke(ae.strokeWidth).numKeys>0){ | |
| pathObject[1].data.animData.strokeWidth=[] | |
| for(var k=0;k<stroke(ae.strokeWidth).numKeys;k++){ | |
| pathObject[1].data.animData.strokeWidth.push({ | |
| time:stroke(ae.strokeWidth).keyTime(k+1), | |
| value:stroke(ae.strokeWidth).keyValue(k+1) | |
| }) | |
| } | |
| } | |
| if(stroke(ae.strokeColor).numKeys>0){ | |
| pathObject[1].data.animData.strokeColor=[] | |
| for(var k=0;k<stroke(ae.strokeColor).numKeys;k++){ | |
| pathObject[1].data.animData.strokeColor.push({ | |
| time:stroke(ae.strokeColor).keyTime(k+1), | |
| value:stroke(ae.strokeColor).keyValue(k+1) | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| catch(err){} | |
| try{ | |
| var fill=itemChild.propertyGroup(1)(ae.fill)(ae.fillColor); | |
| if(typeof fill!="undefined"){ | |
| pathObject[1].fillColor=fill.value | |
| if(fill.numKeys>0){ | |
| pathObject[1].data.animData.fillColor=[] | |
| for(var k=0;k<fill.numKeys;k++){ | |
| pathObject[1].data.animData.fillColor.push({ | |
| time:fill.keyTime(k+1), | |
| value:fill.keyValue(k+1) | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| catch(err){} | |
| obj[1].children.push(pathObject) | |
| } | |
| if(itemChild.numProperties>0 | |
| && itemChild.name!="Material Options" | |
| && itemChild.name!="Geometry Options" | |
| && itemChild.name!="Layer Styles" | |
| ){ | |
| //dumpProps(itemChild,lvl) | |
| } | |
| } | |
| } | |
| var waDuration=app.project.activeItem.workAreaDuration; | |
| var waStart=app.project.activeItem.workAreaStart; | |
| var compDuration=app.project.activeItem.duration; | |
| var loopAnimation=(waStart+waDuration!=compDuration); | |
| projectData=[] | |
| //outputFile = File.saveDialog("Save the text file."); | |
| //if (outputFile != null) { | |
| //outputFile.open("w","TEXT","????"); | |
| var layers=app.project.activeItem.layers; | |
| for(var i=0;i<layers.length;i++){ | |
| var item=layers[i+1] | |
| var layerData=["Layer",{ | |
| applyMatrix:false, | |
| position:[item(ae.transform).position.value[0],item(ae.transform).position.value[1]], | |
| pivot:[item(ae.transform).anchorPoint.value[0],item(ae.transform).anchorPoint.value[1]], | |
| children:[], | |
| data:{ | |
| animData:{ | |
| position:[] | |
| }, | |
| markers:[], | |
| motionPath:[], | |
| expression:'console.log(this)', | |
| animLoop:loopAnimation, | |
| animLength:waDuration, | |
| playing:false, | |
| startTime:0 | |
| } | |
| }]; | |
| var markers = item.property("marker"); | |
| for (var i=1; i<=markers.numKeys; i++){ | |
| var keyName=markers.keyValue(i).comment || 'anim'+i | |
| layerData[1].data.markers.push({ | |
| name:keyName, | |
| start:markers.keyTime(i), | |
| end:markers.keyTime(i)+markers.keyValue(i).duration, | |
| duration:markers.keyValue(i).duration | |
| }) | |
| } | |
| dumpProps(item,0,layerData) | |
| layerData[1].children.reverse() | |
| var itemPos=item(ae.transform).position; | |
| for(var k=0;k<itemPos.numKeys;k++){ | |
| var pt0=[itemPos.keyValue(Math.max(1,k))[0],itemPos.keyValue(Math.max(1,k))[1]]; | |
| var pt1=[itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]]; | |
| var pt2=[itemPos.keyValue(Math.min(itemPos.numKeys,k+2))[0],itemPos.keyValue(Math.min(itemPos.numKeys,k+2))[1]]; | |
| var pt01=[pt1[0]-pt0[0],pt1[1]-pt0[1]] | |
| var pt12=[pt2[0]-pt1[0],pt2[1]-pt1[1]] | |
| var inDelta=Math.sqrt( | |
| Math.pow(pt01[0],2)+ | |
| Math.pow(pt01[1],2) | |
| ) | |
| var outDelta=Math.sqrt( | |
| Math.pow(pt12[0],2)+ | |
| Math.pow(pt12[1],2) | |
| ) | |
| var easeInX=1-itemPos.keyInTemporalEase(k+1)[0].influence/100 | |
| var easeInY=1-(itemPos.keyInTemporalEase(k+1)[0].influence*itemPos.keyInTemporalEase(k+1)[0].speed/inDelta/100) | |
| var easeOutX=itemPos.keyOutTemporalEase(k+1)[0].influence/100 | |
| var easeOutY=itemPos.keyOutTemporalEase(k+1)[0].influence*itemPos.keyOutTemporalEase(k+1)[0].speed/outDelta/100 | |
| layerData[1].data.animData.position.push({ | |
| time:itemPos.keyTime(k+1), | |
| value:[itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]], | |
| easeIn: [easeInX,easeInY], | |
| easeOut: [easeOutX,easeOutY], | |
| index:k | |
| }) | |
| layerData[1].data.motionPath.push([ | |
| [itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]], | |
| [itemPos.keyInSpatialTangent(k+1)[0],itemPos.keyInSpatialTangent(k+1)[1]], | |
| [itemPos.keyOutSpatialTangent(k+1)[0],itemPos.keyOutSpatialTangent(k+1)[1]], | |
| ]) | |
| } | |
| projectData.push(layerData) | |
| //outputFile.writeln(JSON.stringify(item.property("Contents"))) | |
| } | |
| var animData=JSON.stringify(projectData); | |
| prompt('Animation data as PaperJS project:', animData) | |
| // var str="\"helloror\"" | |
| // system.callSystem("osascript -e 'tell application \"Finder\" to set the clipboard to \""+animData+"\"'") | |
| // system.callSystem("osascript -e 'tell application \"Finder\" to set the clipboard to "+animData+"'") | |
| //set the clipboard to "Some text" | |
| //system.callSystem("osascript -e \'tell application \"Finder\"\rset the clipboard to \"" + animData +"\"\rend tell\'"); | |
| //outputFile.writeln(JSON.stringify(projectData)) | |
| //outputFile.close(); | |
| //outputFile.execute(); | |
| //} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| include('http://eerojohannes.com/js/bezier-easing.js') | |
| document.addEventListener('paste', function(evt) { | |
| if(document.activeElement.nodeName!="TEXTAREA"){ | |
| var str=evt.clipboardData.getData('text/plain'); | |
| var jsoni=project.importJSON(str) | |
| for(layer of jsoni){ | |
| if(layer.data.motionPath!=null){ | |
| if(typeof layer.data.motionPath.className=="undefined"){ | |
| layer.data.motionPath=new Path({ | |
| segments:layer.data.motionPath, | |
| insert:false | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| }) | |
| function lerp(obj1,obj2,prop,fac){ | |
| return (obj2[prop]-obj1[prop])*fac+obj1[prop] | |
| } | |
| function lerpArray(arr1,arr2,fac){ | |
| return arr1.map( (obj,i,arr) => (arr2[i]-arr1[i])*fac+arr1[i] ) | |
| } | |
| function lerpSegments(segs1,segs2,fac){ | |
| return segs1.map( (o,i,a) => | |
| segs1[i].map( (p,j,b) => | |
| [ | |
| (segs2[i][j][0]-segs1[i][j][0])*fac+segs1[i][j][0], | |
| (segs2[i][j][1]-segs1[i][j][1])*fac+segs1[i][j][1] | |
| ] | |
| ) | |
| ) | |
| } | |
| Item.prototype.scrub=function(time, time2){ | |
| let propKeys=Object.keys(this.data.animData) | |
| for(var i=0;i<propKeys.length;i++){ | |
| var prop=propKeys[i]; | |
| var keys=this.data.animData[prop] | |
| //first key | |
| if(time<keys[0].time){ | |
| let key1=keys[0] | |
| this[prop]=key1.value | |
| } | |
| else if(time>=keys[0].time && time<keys[keys.length-1].time){ | |
| var currentKeys=keys.filter( | |
| (obj,idx,arr) => | |
| time >= arr[Math.max(0,idx-1)].time | |
| ) | |
| if(currentKeys.length>1){ //console.log('arrrr') | |
| let key1=currentKeys[currentKeys.length-2] | |
| let key2=currentKeys[currentKeys.length-1] | |
| let delta=key2.time-key1.time | |
| let pos=time-key1.time | |
| let fac=Math.min(1,Math.max(0,pos/delta)) | |
| if(key1.hasOwnProperty('easeIn') && key1.hasOwnProperty('easeOut') && | |
| key2.hasOwnProperty('easeIn') && key2.hasOwnProperty('easeOut')){ | |
| var ease=BezierEasing(key1.easeOut[0],key1.easeOut[1],key2.easeIn[0],key2.easeIn[1]) | |
| var eased=ease(fac) | |
| } | |
| else { | |
| var eased=fac | |
| } | |
| if(prop=="position"){ | |
| if(this.className=="Layer"){ | |
| let v=lerpArray(key1.value,key2.value,eased) | |
| /* | |
| let curveLength=this.data.motionPath.curves[key1.index].length | |
| let curveOffset=this.data.motionPath.getOffsetOf(this.data.motionPath.curves[key1.index].point1) | |
| this.position=this.data.motionPath.getPointAt(ease(fac)*curveLength+curveOffset); | |
| */ | |
| this.position=v | |
| } | |
| if(this.className=="Group"){ | |
| let v=lerpArray(key1.value,key2.value,eased) | |
| this.position=v | |
| } | |
| } | |
| else if(prop=="fillColor" || prop=="strokeColor"){ | |
| let v=lerpArray(key1.value,key2.value,eased) | |
| this[prop]=v | |
| } | |
| else if(prop=="segments"){ | |
| let v=lerpSegments(key1.value,key2.value,eased) | |
| this[prop]=v | |
| } | |
| else { | |
| let v=lerp(key1,key2,'value',eased) | |
| this[prop]=v | |
| } | |
| } | |
| } | |
| //last key | |
| else{ | |
| let key1=keys[keys.length-1] | |
| this[prop]=key1.value | |
| //if(this.className=="Layer" && prop=="position"){ | |
| } | |
| } | |
| } | |
| Item.prototype.multiScrub=function(time,time2,midPoint){ | |
| let propKeys=Object.keys(this.data.animData) | |
| for(var i=0;i<propKeys.length;i++){ | |
| var prop=propKeys[i]; | |
| var keys=this.data.animData[prop] | |
| //console.log(time,time2) | |
| if(time>=keys[0].time && time<keys[keys.length-1].time){ | |
| var currentKeys=keys.filter( | |
| (obj,idx,arr) => | |
| time >= arr[Math.max(0,idx-1)].time | |
| ) | |
| var currentKeysB=keys.filter( | |
| (obj,idx,arr) => | |
| time2 >= arr[Math.max(0,idx-1)].time | |
| ) | |
| if(currentKeys.length>1){ | |
| let key1=currentKeys[currentKeys.length-2] | |
| let key2=currentKeys[currentKeys.length-1] | |
| let delta=key2.time-key1.time | |
| let pos=time-key1.time | |
| let fac=Math.min(1,Math.max(0,pos/delta)) | |
| if(key1.hasOwnProperty('easeIn') && key1.hasOwnProperty('easeOut') && | |
| key2.hasOwnProperty('easeIn') && key2.hasOwnProperty('easeOut')){ | |
| var ease=BezierEasing(key1.easeOut[0],key1.easeOut[1],key2.easeIn[0],key2.easeIn[1]) | |
| var eased=ease(fac) | |
| } | |
| else { | |
| var eased=fac | |
| } | |
| let key1B=currentKeysB[currentKeysB.length-2] | |
| let key2B=currentKeysB[currentKeysB.length-1] | |
| let deltaB=key2B.time-key1B.time | |
| let posB=time2-key1B.time | |
| let facB=Math.min(1,Math.max(0,posB/deltaB)) | |
| if(key1B.hasOwnProperty('easeIn') && key1B.hasOwnProperty('easeOut') && | |
| key2B.hasOwnProperty('easeIn') && key2B.hasOwnProperty('easeOut')){ | |
| var easeB=BezierEasing(key1B.easeOut[0],key1B.easeOut[1],key2B.easeIn[0],key2B.easeIn[1]) | |
| var easedB=easeB(facB) | |
| } | |
| else { | |
| var easedB=facB | |
| } | |
| if(prop=="segments"){ | |
| let v=lerpSegments(key1.value,key2.value,eased) | |
| let v2=lerpSegments(key1B.value,key2B.value,easedB) | |
| let v3=lerpSegments(v,v2,midPoint) | |
| this[prop]=v3 | |
| } | |
| } | |
| } | |
| } | |
| } | |
| paths=0 | |
| numKeys=3 | |
| playing=true | |
| view.startTime=0 | |
| view.time=0 | |
| view.playhead=0 | |
| view.timelineLength=3.0 | |
| function onFrame(e){ | |
| view.time=e.time | |
| view.playhead=(e.time-view.startTime)%view.timelineLength; | |
| var animLayers=project.layers.filter( layer => layer.data.playing==true) | |
| for(var layer of animLayers){ | |
| var layerTime=( | |
| (view.time-layer.data.startTime)%layer.data.animLength | |
| ) | |
| +layer.data.animStart; | |
| var layerTimeB=( | |
| (view.time-layer.data.startTime)%layer.data.animLengthB | |
| ) | |
| +layer.data.animStartB; | |
| for(var item of layer.children){ | |
| if(item.data.animData!=null){ | |
| item.multiScrub(layerTime,layerTimeB) | |
| } | |
| if(item.children!=null){ | |
| for(var subitem of item.children){ | |
| if(subitem.data.animData!=null){ | |
| subitem.scrub(layerTime) | |
| //subitem.multiScrub(layerTime,layerTimeB,mousePos.y/view.viewSize.height) | |
| } | |
| } | |
| } | |
| if(layer.data.animData!=null){ | |
| //layer.multiScrub(layerTime) | |
| } | |
| /* | |
| if(layer.data.expression!=null){ | |
| var result=function(str){return eval(str)} | |
| .call(layer,layer.data.expression); | |
| } | |
| */ | |
| } | |
| } | |
| } | |
| var t=new Tool() | |
| t.on({ | |
| mousedown(){ | |
| var it=project.layers[0]; | |
| it.data.playing=true | |
| it.data.startTime=view.time | |
| //console.log(it.data) | |
| }, | |
| mousemove(e){ | |
| mousePos=e.point | |
| }, | |
| mouseup(){ | |
| project.layers[0].data.playing=false | |
| }, | |
| keydown(e){ | |
| var it=project.layers[0]; | |
| if(!e.modifiers.command){ | |
| if(!isNaN(parseInt(e.key))){ | |
| var num=parseInt(e.key)-1 | |
| if(it.data.markers[num]!=null){ | |
| it.data.startTime=view.time | |
| it.data.animStart=it.data.markers[num].start | |
| it.data.animEnd=it.data.markers[num].end | |
| it.data.animLength=it.data.markers[num].duration | |
| if(num<2){ | |
| it.data.animStartB=it.data.markers[num+1].start | |
| it.data.animEndB=it.data.markers[num+1].end | |
| it.data.animLengthB=it.data.markers[num+1].duration | |
| } | |
| it.data.playing=true | |
| } | |
| } | |
| /* | |
| var m=it.data.markers.filter( | |
| x => x.name=="walk" | |
| )[0] | |
| */ | |
| } | |
| } | |
| }) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment