添加three.js官方动画编辑器。

This commit is contained in:
liteng 2018-09-20 21:24:34 +08:00
parent dcc86505a2
commit 3d5418e69f
18 changed files with 2605 additions and 0 deletions

View File

@ -0,0 +1,217 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Code = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setId( 'effect' );
container.setPosition( 'absolute' );
container.setBackgroundColor( '#272822' );
container.setDisplay( 'none' );
var header = new UI.Panel();
header.setPadding( '10px' );
container.add( header );
var title = new UI.Text().setColor( '#fff' );
header.add( title );
var buttonSVG = ( function () {
var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
svg.setAttribute( 'width', 32 );
svg.setAttribute( 'height', 32 );
var path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
path.setAttribute( 'd', 'M 12,12 L 22,22 M 22,12 12,22' );
path.setAttribute( 'stroke', '#fff' );
svg.appendChild( path );
return svg;
} )();
var close = new UI.Element( buttonSVG );
close.setPosition( 'absolute' );
close.setTop( '3px' );
close.setRight( '1px' );
close.setCursor( 'pointer' );
close.onClick( function () {
container.setDisplay( 'none' );
} );
header.add( close );
var delay;
var errorLine = null;
var currentEffect = null;
var currentInclude = null;
var codemirror = CodeMirror( container.dom, {
value: '',
lineNumbers: true,
lineWrapping: true,
matchBrackets: true,
indentWithTabs: true,
tabSize: 4,
indentUnit: 4,
mode: 'javascript'
} );
codemirror.setOption( 'theme', 'monokai' );
codemirror.on( 'change', function () {
if ( codemirror.state.focused === false ) return;
clearTimeout( delay );
delay = setTimeout( function () {
if ( errorLine ) {
codemirror.removeLineClass( errorLine, 'CodeMirror-errorLine' );
errorLine = null;
}
if ( currentInclude !== null ) {
currentInclude.source = codemirror.getValue();
editor.signals.includeChanged.dispatch();
} else if ( currentEffect !== null ) {
var error;
var currentSource = currentEffect.source;
editor.timeline.reset();
try {
currentEffect.source = codemirror.getValue();
editor.compileEffect( currentEffect );
} catch ( e ) {
error = e.name + ' : ' + e.message; // e.stack, e.columnNumber, e.lineNumber
if ( /Chrome/i.test( navigator.userAgent ) ) {
var result = /<anonymous>:([0-9]+):([0-9+])/g.exec( e.stack );
if ( result !== null ) errorLine = parseInt( result[ 1 ] ) - 3;
} else if ( /Firefox/i.test( navigator.userAgent ) ) {
var result = /Function:([0-9]+):([0-9+])/g.exec( e.stack );
if ( result !== null ) errorLine = parseInt( result[ 1 ] ) - 1;
}
if ( errorLine !== null ) {
codemirror.addLineClass( errorLine, 'errorLine', 'CodeMirror-errorLine' );
}
}
editor.timeline.update( editor.player.currentTime );
if ( error !== undefined ) {
errorDiv.setDisplay( '' );
errorText.setValue( '⌦ ' + error );
currentEffect.source = currentSource;
} else {
errorDiv.setDisplay( 'none' );
}
}
}, 1000 );
} );
var wrapper = codemirror.getWrapperElement();
wrapper.addEventListener( 'keydown', function ( event ) {
event.stopPropagation();
} );
//
var errorDiv = new UI.Div();
errorDiv.setPosition( 'absolute' );
errorDiv.setDisplay( 'none' );
errorDiv.setTop( '8px' );
errorDiv.setWidth( '100%' );
errorDiv.setTextAlign( 'center' );
errorDiv.setZIndex( '3' );
container.add( errorDiv );
var errorText = new UI.Text();
errorText.setBackgroundColor( '#f00' );
errorText.setColor( '#fff' );
errorText.setPadding( '4px' );
errorDiv.add( errorText );
//
signals.editorCleared.add( function () {
container.setDisplay( 'none' );
} );
signals.effectSelected.add( function ( effect ) {
container.setDisplay( '' );
title.setValue( effect.name );
codemirror.setValue( effect.source );
codemirror.clearHistory();
currentEffect = effect;
currentInclude = null;
} );
signals.includeSelected.add( function ( include ) {
container.setDisplay( '' );
title.setValue( include.name );
codemirror.setValue( include.source );
codemirror.clearHistory();
currentEffect = null;
currentInclude = include;
} );
editor.signals.animationSelected.add( function ( animation ) {
if ( animation === null ) return;
var effect = animation.effect;
title.setValue( effect.name );
codemirror.setValue( effect.source );
codemirror.clearHistory();
currentEffect = effect;
currentInclude = null;
} );
return container;
};

View File

@ -0,0 +1,53 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Config = function () {
var name = 'framejs-editor';
var storage = {};
if ( window.localStorage[ name ] !== undefined ) {
var data = JSON.parse( window.localStorage[ name ] );
for ( var key in data ) {
storage[ key ] = data[ key ];
}
}
return {
getKey: function ( key ) {
return storage[ key ];
},
setKey: function () { // key, value, key, value ...
for ( var i = 0, l = arguments.length; i < l; i += 2 ) {
storage[ arguments[ i ] ] = arguments[ i + 1 ];
}
window.localStorage[ name ] = JSON.stringify( storage );
console.log( '[' + /\d\d\:\d\d\:\d\d/.exec( new Date() )[ 0 ] + ']', 'Saved config to LocalStorage.' );
},
clear: function () {
delete window.localStorage[ name ];
}
};
};

View File

@ -0,0 +1,133 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Controls = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setId( 'controls' );
var row = new UI.Row();
row.setPadding( '6px' );
container.add( row );
var prevButton = new UI.Button();
prevButton.setBackground( 'url(files/prev.svg)' );
prevButton.setWidth( '20px' );
prevButton.setHeight( '20px' );
prevButton.setMarginRight( '4px' );
prevButton.setVerticalAlign( 'middle' );
prevButton.onClick( function () {
editor.setTime( editor.player.currentTime - 1 );
} );
row.add( prevButton );
var playButton = new UI.Button();
playButton.setBackground( 'url(files/play.svg)' );
playButton.setWidth( '20px' );
playButton.setHeight( '20px' );
playButton.setMarginRight( '4px' );
playButton.setVerticalAlign( 'middle' );
playButton.onClick( function () {
editor.player.isPlaying ? editor.stop() : editor.play();
} );
row.add( playButton );
var nextButton = new UI.Button();
nextButton.setBackground( 'url(files/next.svg)' );
nextButton.setWidth( '20px' );
nextButton.setHeight( '20px' );
nextButton.setMarginRight( '4px' );
nextButton.setVerticalAlign( 'middle' );
nextButton.onClick( function () {
editor.setTime( editor.player.currentTime + 1 );
} );
row.add( nextButton );
function ignoreKeys( event ) {
switch ( event.keyCode ) {
case 13: case 32: event.preventDefault();
}
};
prevButton.onKeyDown( ignoreKeys );
playButton.onKeyDown( ignoreKeys );
nextButton.onKeyDown( ignoreKeys );
var timeText = new UI.Text();
timeText.setColor( '#bbb' );
timeText.setWidth( '60px' );
timeText.setMarginLeft( '10px' );
timeText.setValue( '0:00.00' );
row.add( timeText );
function updateTimeText( value ) {
var minutes = Math.floor( value / 60 );
var seconds = value % 60;
var padding = seconds < 10 ? '0' : '';
timeText.setValue( minutes + ':' + padding + seconds.toFixed( 2 ) );
}
var playbackRateText = new UI.Text();
playbackRateText.setColor( '#999' );
playbackRateText.setMarginLeft( '8px' );
playbackRateText.setValue( '1.0x' );
row.add( playbackRateText );
function updatePlaybackRateText( value ) {
playbackRateText.setValue( value.toFixed( 1 ) + 'x' );
}
var fullscreenButton = new UI.Button();
fullscreenButton.setBackground( 'url(files/fullscreen.svg)' );
fullscreenButton.setWidth( '20px' );
fullscreenButton.setHeight( '20px' );
fullscreenButton.setFloat( 'right' );
fullscreenButton.setVerticalAlign( 'middle' );
fullscreenButton.onClick( function () {
editor.signals.fullscreen.dispatch();
} );
row.add( fullscreenButton );
//
signals.playingChanged.add( function ( isPlaying ) {
playButton.setBackground( isPlaying ? 'url(files/pause.svg)' : 'url(files/play.svg)' )
} );
signals.playbackRateChanged.add( function ( value ) {
updatePlaybackRateText( value );
} );
signals.timeChanged.add( function ( value ) {
updateTimeText( value );
} );
return container;
};

View File

@ -0,0 +1,583 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Editor = function () {
var Signal = signals.Signal;
this.signals = {
editorCleared: new Signal(),
// libraries
libraryAdded: new Signal(),
// includes
includeAdded: new Signal(),
includeSelected: new Signal(),
includeChanged: new Signal(),
includeRemoved: new Signal(),
includesCleared: new Signal(),
// effects
effectAdded: new Signal(),
effectRenamed: new Signal(),
effectRemoved: new Signal(),
effectSelected: new Signal(),
effectCompiled: new Signal(),
// actions
fullscreen: new Signal(),
exportState: new Signal(),
// animations
animationRenamed: new Signal(),
animationAdded: new Signal(),
animationModified: new Signal(),
animationRemoved: new Signal(),
animationSelected: new Signal(),
// curves
curveAdded: new Signal(),
// events
playingChanged: new Signal(),
playbackRateChanged: new Signal(),
timeChanged: new Signal(),
timelineScaled: new Signal(),
windowResized: new Signal()
};
this.config = new Config();
this.player = new FRAME.Player();
this.resources = new FRAME.Resources();
this.duration = 500;
this.libraries = [];
this.includes = [];
this.effects = [];
this.timeline = new FRAME.Timeline();
this.selected = null;
// signals
var scope = this;
this.signals.animationModified.add( function () {
scope.timeline.reset();
scope.timeline.sort();
try {
scope.timeline.update( scope.player.currentTime );
} catch ( e ) {
console.error( e );
}
} );
this.signals.effectCompiled.add( function () {
try {
scope.timeline.update( scope.player.currentTime );
} catch ( e ) {
console.error( e );
}
} );
this.signals.timeChanged.add( function () {
try {
scope.timeline.update( scope.player.currentTime );
} catch ( e ) {
console.error( e );
}
} );
// Animate
var prevTime = 0;
function animate( time ) {
scope.player.tick( time - prevTime );
if ( scope.player.isPlaying ) {
scope.signals.timeChanged.dispatch( scope.player.currentTime );
}
prevTime = time;
requestAnimationFrame( animate );
}
requestAnimationFrame( animate );
};
Editor.prototype = {
play: function () {
this.player.play();
this.signals.playingChanged.dispatch( true );
},
stop: function () {
this.player.pause();
this.signals.playingChanged.dispatch( false );
},
speedUp: function () {
this.player.playbackRate += 0.1;
this.signals.playbackRateChanged.dispatch( this.player.playbackRate );
},
speedDown: function () {
this.player.playbackRate -= 0.1;
this.signals.playbackRateChanged.dispatch( this.player.playbackRate );
},
setTime: function ( time ) {
// location.hash = time;
this.player.currentTime = Math.max( 0, time );
this.signals.timeChanged.dispatch( this.player.currentTime );
},
// libraries
addLibrary: function ( url, content ) {
var script = document.createElement( 'script' );
script.id = 'library-' + this.libraries.length;
script.textContent = content;
document.head.appendChild( script );
this.libraries.push( url );
this.signals.libraryAdded.dispatch();
},
// includes
addInclude: function ( name, source ) {
try {
new Function( 'resources', source )( this.resources );
} catch ( e ) {
console.error( e );
}
this.includes.push( { name: name, source: source } );
this.signals.includeAdded.dispatch();
},
removeInclude: function ( include ) {
var index = this.includes.indexOf( include );
this.includes.splice( index, 1 );
this.signals.includeRemoved.dispatch();
},
selectInclude: function ( include ) {
this.signals.includeSelected.dispatch( include );
},
reloadIncludes: function () {
var includes = this.includes;
this.signals.includesCleared.dispatch();
for ( var i = 0; i < includes.length; i ++ ) {
var include = includes[ i ];
try {
new Function( 'resources', include.source )( this.resources );
} catch ( e ) {
console.error( e );
}
}
},
// effects
addEffect: function ( effect ) {
this.effects.push( effect );
this.signals.effectAdded.dispatch( effect );
},
selectEffect: function ( effect ) {
this.signals.effectSelected.dispatch( effect );
},
removeEffect: function ( effect ) {
var index = this.effects.indexOf( effect );
if ( index >= 0 ) {
this.effects.splice( index, 1 );
this.signals.effectRemoved.dispatch( effect );
}
},
compileEffect: function ( effect ) {
try {
effect.compile( this.resources, this.player );
} catch ( e ) {
console.error( e );
}
this.signals.effectCompiled.dispatch( effect );
},
// Remove any effects that are not bound to any animations.
cleanEffects: function () {
var scope = this;
var effects = this.effects.slice( 0 );
var animations = this.timeline.animations;
effects.forEach( function ( effect, i ) {
var bound = false;
for ( var j = 0; j < animations.length; j++ ) {
var animation = animations[ j ];
if ( animation.effect === effect ) {
bound = true;
break;
}
}
if ( bound === false ) {
scope.removeEffect( effect );
}
} );
},
// animations
addAnimation: function ( animation ) {
var effect = animation.effect;
if ( effect.program === null ) {
this.compileEffect( effect );
}
this.timeline.add( animation );
this.signals.animationAdded.dispatch( animation );
},
selectAnimation: function ( animation ) {
if ( this.selected === animation ) return;
this.selected = animation;
this.signals.animationSelected.dispatch( animation );
},
removeAnimation: function ( animation ) {
this.timeline.remove( animation );
this.signals.animationRemoved.dispatch( animation );
},
addCurve: function ( curve ) {
this.timeline.curves.push( curve );
this.signals.curveAdded.dispatch( curve );
},
clear: function () {
this.libraries = [];
this.includes = [];
this.effects = [];
while ( this.timeline.animations.length > 0 ) {
this.removeAnimation( this.timeline.animations[ 0 ] );
}
this.signals.editorCleared.dispatch();
},
fromJSON: function ( json ) {
function loadFile( url, onLoad ) {
var request = new XMLHttpRequest();
request.open( 'GET', url, true );
request.addEventListener( 'load', function ( event ) {
onLoad( event.target.response );
} );
request.send( null );
}
function loadLibraries( libraries, onLoad ) {
var count = 0;
function loadNext() {
if ( count === libraries.length ) {
onLoad();
return;
}
var url = libraries[ count ++ ];
loadFile( url, function ( content ) {
scope.addLibrary( url, content );
loadNext();
} );
}
loadNext();
}
var scope = this;
var libraries = json.libraries || [];
loadLibraries( libraries, function () {
var includes = json.includes;
for ( var i = 0, l = includes.length; i < l; i ++ ) {
var data = includes[ i ];
var name = data[ 0 ];
var source = data[ 1 ];
if ( Array.isArray( source ) ) source = source.join( '\n' );
scope.addInclude( name, source );
}
var effects = json.effects;
for ( var i = 0, l = effects.length; i < l; i ++ ) {
var data = effects[ i ];
var name = data[ 0 ];
var source = data[ 1 ];
if ( Array.isArray( source ) ) source = source.join( '\n' );
scope.addEffect( new FRAME.Effect( name, source ) );
}
var animations = json.animations;
for ( var i = 0, l = animations.length; i < l; i ++ ) {
var data = animations[ i ];
var animation = new FRAME.Animation(
data[ 0 ],
data[ 1 ],
data[ 2 ],
data[ 3 ],
scope.effects[ data[ 4 ] ],
data[ 5 ]
);
scope.addAnimation( animation );
}
scope.setTime( 0 );
} );
},
toJSON: function () {
var json = {
"config": {},
"libraries": this.libraries.slice(),
"includes": [],
"effects": [],
// "curves": [],
"animations": []
};
/*
// curves
var curves = this.timeline.curves;
for ( var i = 0, l = curves.length; i < l; i ++ ) {
var curve = curves[ i ];
if ( curve instanceof FRAME.Curves.Linear ) {
json.curves.push( [ 'linear', curve.points ] );
}
}
*/
// includes
var includes = this.includes;
for ( var i = 0, l = includes.length; i < l; i ++ ) {
var include = includes[ i ];
var name = include.name;
var source = include.source;
json.includes.push( [ name, source.split( '\n' ) ] );
}
// effects
var effects = this.effects;
for ( var i = 0, l = effects.length; i < l; i ++ ) {
var effect = effects[ i ];
var name = effect.name;
var source = effect.source;
json.effects.push( [ name, source.split( '\n' ) ] );
}
// animations
var animations = this.timeline.animations;
for ( var i = 0, l = animations.length; i < l; i ++ ) {
var animation = animations[ i ];
var effect = animation.effect;
/*
var parameters = {};
for ( var key in module.parameters ) {
parameters[ key ] = module.parameters[ key ].value;
}
*/
json.animations.push( [
animation.name,
animation.start,
animation.end,
animation.layer,
this.effects.indexOf( animation.effect ),
animation.enabled
] );
}
return json;
}
};

View File

@ -0,0 +1,65 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Menubar.Edit = function ( editor ) {
var container = new UI.Panel();
container.setClass( 'menu' );
var title = new UI.Panel();
title.setClass( 'title' );
title.setTextContent( 'Edit' );
container.add( title );
//
var options = new UI.Panel();
options.setClass( 'options' );
container.add( options );
// duplicate
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Duplicate' );
option.onClick( function () {
if ( editor.selected === null ) return;
var selected = editor.selected;
var offset = selected.end - selected.start;
var animation = new FRAME.Animation(
selected.name,
selected.start + offset,
selected.end + offset,
selected.layer,
selected.effect
);
editor.addAnimation( animation );
editor.selectAnimation( animation );
} );
options.add( option );
// remove
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Remove' );
option.onClick( function () {
if ( editor.selected === null ) return;
editor.removeAnimation( editor.selected );
editor.selectAnimation( null );
} );
options.add( option );
return container;
};

View File

@ -0,0 +1,62 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Menubar.Examples = function ( editor ) {
var container = new UI.Panel();
container.setClass( 'menu' );
var title = new UI.Panel();
title.setClass( 'title' );
title.setTextContent( 'Examples' );
container.add( title );
var options = new UI.Panel();
options.setClass( 'options' );
container.add( options );
// Examples
var items = [
{ title: 'HTML Colors', file: 'html_colors.json' },
{ title: 'HTML Loop', file: 'html_loop.json' },
{ title: 'Three.js Cube', file: 'threejs_cube.json' },
{ title: 'Three.js Shaders', file: 'threejs_shaders.json' }
];
for ( var i = 0; i < items.length; i ++ ) {
( function ( i ) {
var item = items[ i ];
var option = new UI.Row();
option.setClass( 'option' );
option.setTextContent( item.title );
option.onClick( function () {
if ( confirm( 'Any unsaved data will be lost. Are you sure?' ) ) {
var request = new XMLHttpRequest();
request.open( 'GET', '../examples/' + item.file, true );
request.addEventListener( 'load', function ( event ) {
editor.clear();
editor.fromJSON( JSON.parse( event.target.responseText ) );
}, false );
request.send( null );
}
} );
options.add( option );
} )( i )
}
return container;
};

View File

@ -0,0 +1,92 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Menubar.File = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setClass( 'menu' );
var title = new UI.Panel();
title.setClass( 'title' );
title.setTextContent( 'File' );
container.add( title );
var options = new UI.Panel();
options.setClass( 'options' );
container.add( options );
// New
var option = new UI.Row();
option.setClass( 'option' );
option.setTextContent( 'New' );
option.onClick( function () {
if ( confirm( 'Any unsaved data will be lost. Are you sure?' ) ) {
editor.clear();
}
} );
options.add( option );
// import
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Import' );
option.onClick( Import );
options.add( option );
var fileInput = document.createElement( 'input' );
fileInput.type = 'file';
fileInput.addEventListener( 'change', function ( event ) {
var reader = new FileReader();
reader.addEventListener( 'load', function ( event ) {
editor.clear();
editor.fromJSON( JSON.parse( event.target.result ) );
}, false );
reader.readAsText( fileInput.files[ 0 ] );
} );
function Import () {
if ( confirm( 'Any unsaved data will be lost. Are you sure?' ) )
fileInput.click();
}
// export
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Export' );
option.onClick( Export );
options.add( option );
signals.exportState.add( Export );
function Export () {
var output = JSON.stringify( editor.toJSON(), null, '\t' );
var blob = new Blob( [ output ], { type: 'text/plain' } );
var objectURL = URL.createObjectURL( blob );
window.open( objectURL, '_blank' );
window.focus();
}
return container;
};

View File

@ -0,0 +1,41 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Menubar.Help = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setClass( 'menu' );
var title = new UI.Panel();
title.setClass( 'title' );
title.setTextContent( 'Help' );
container.add( title );
//
var options = new UI.Panel();
options.setClass( 'options' );
container.add( options );
// source code
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Source code' );
option.onClick( function () { window.open( 'https://github.com/mrdoob/frame.js/tree/master/editor', '_blank' ) } );
options.add( option );
// about
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'About' );
option.onClick( function () { window.open( 'http://github.com/mrdoob/frame.js/', '_blank' ) } );
options.add( option );
return container;
}

View File

@ -0,0 +1,35 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Menubar.View = function ( editor ) {
var container = new UI.Panel();
container.setClass( 'menu' );
var title = new UI.Panel();
title.setClass( 'title' );
title.setTextContent( 'View' );
container.add( title );
//
var options = new UI.Panel();
options.setClass( 'options' );
container.add( options );
// remove
var option = new UI.Panel();
option.setClass( 'option' );
option.setTextContent( 'Fullscreen' );
option.onClick( function () {
editor.signals.fullscreen.dispatch();
} );
options.add( option );
return container;
}

View File

@ -0,0 +1,17 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Menubar = function ( editor ) {
var container = new UI.Panel();
container.setId( 'menubar' );
container.add( new Menubar.File( editor ) );
container.add( new Menubar.Edit( editor ) );
container.add( new Menubar.Examples( editor ) );
container.add( new Menubar.Help( editor ) );
return container;
}

View File

@ -0,0 +1,349 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Sidebar.Animation = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setId( 'animation' );
//
var selected = null;
var values;
function createParameterRow( key, parameter ) {
if ( parameter === null ) return;
var parameterRow = new UI.Row();
parameterRow.add( new UI.Text( parameter.name ).setWidth( '90px' ) );
if ( parameter instanceof FRAME.Parameters.Boolean ) {
var parameterValue = new UI.Checkbox()
.setValue( parameter.value )
.onChange( function () {
parameter.value = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( parameterValue );
values[ key ] = parameterValue;
} else if ( parameter instanceof FRAME.Parameters.Integer ) {
var parameterValue = new UI.Integer()
.setRange( parameter.min, parameter.max )
.setValue( parameter.value )
.setWidth( '150px' )
.onChange( function () {
parameter.value = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( parameterValue );
values[ key ] = parameterValue;
} else if ( parameter instanceof FRAME.Parameters.Float ) {
var parameterValue = new UI.Number()
.setRange( parameter.min, parameter.max )
.setValue( parameter.value )
.setWidth( '150px' )
.onChange( function () {
parameter.value = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( parameterValue );
values[ key ] = parameterValue;
} else if ( parameter instanceof FRAME.Parameters.Vector2 ) {
var vectorX = new UI.Number()
.setValue( parameter.value[ 0 ] )
.setWidth( '50px' )
.onChange( function () {
parameter.value[ 0 ] = this.getValue();
signals.animationModified.dispatch( selected );
} );
var vectorY = new UI.Number()
.setValue( parameter.value[ 1 ] )
.setWidth( '50px' )
.onChange( function () {
parameter.value[ 1 ] = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( vectorX );
parameterRow.add( vectorY );
} else if ( parameter instanceof FRAME.Parameters.Vector3 ) {
var vectorX = new UI.Number()
.setValue( parameter.value[ 0 ] )
.setWidth( '50px' )
.onChange( function () {
parameter.value[ 0 ] = this.getValue();
signals.animationModified.dispatch( selected );
} );
var vectorY = new UI.Number()
.setValue( parameter.value[ 1 ] )
.setWidth( '50px' )
.onChange( function () {
parameter.value[ 1 ] = this.getValue();
signals.animationModified.dispatch( selected );
} );
var vectorZ = new UI.Number()
.setValue( parameter.value[ 2 ] )
.setWidth( '50px' )
.onChange( function () {
parameter.value[ 2 ] = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( vectorX );
parameterRow.add( vectorY );
parameterRow.add( vectorZ );
} else if ( parameter instanceof FRAME.Parameters.String ) {
var parameterValue = new UI.Input()
.setValue( parameter.value )
.setWidth( '150px' )
.onKeyUp( function () {
parameter.value = this.getValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( parameterValue );
} else if ( parameter instanceof FRAME.Parameters.Color ) {
var parameterValue = new UI.Color()
.setHexValue( parameter.value )
.setWidth( '150px' )
.onChange( function () {
parameter.value = this.getHexValue();
signals.animationModified.dispatch( selected );
} );
parameterRow.add( parameterValue );
}
return parameterRow;
}
function build() {
container.clear();
if ( selected === null ) return;
values = {};
// Name
var row = new UI.Row();
row.add( new UI.Text( 'Name' ).setWidth( '90px' ) );
container.add( row );
var animationName = new UI.Input( selected.name )
animationName.onChange( function () {
selected.name = this.getValue();
signals.animationRenamed.dispatch( selected );
} );
row.add( animationName );
// Time
var row = new UI.Row();
row.add( new UI.Text( 'Time' ).setWidth( '90px' ) );
container.add( row );
var animationStart = new UI.Number( selected.start ).setWidth( '80px' );
animationStart.onChange( function () {
selected.start = this.getValue();
signals.animationModified.dispatch( selected );
} );
row.add( animationStart );
var animationEnd = new UI.Number( selected.end ).setWidth( '80px' );
animationEnd.onChange( function () {
selected.end = this.getValue();
signals.animationModified.dispatch( selected );
} );
row.add( animationEnd );
// Layer
var row = new UI.Row();
row.add( new UI.Text( 'Layer' ).setWidth( '90px' ) );
container.add( row );
var animationLayer = new UI.Integer( selected.layer ).setWidth( '80px' );
animationLayer.onChange( function () {
selected.layer = this.getValue();
signals.animationModified.dispatch( selected );
} );
row.add( animationLayer );
// Enabled
var row = new UI.Row();
row.add( new UI.Text( 'Enabled' ).setWidth( '90px' ) );
container.add( row );
var animationEnabled = new UI.Checkbox( selected.enabled )
animationEnabled.onChange( function () {
selected.enabled = this.getValue();
signals.animationModified.dispatch( selected );
} );
row.add( animationEnabled );
//
container.add( new UI.HorizontalRule().setMargin( '20px 0px' ) );
//
var row = new UI.Row();
row.add( new UI.Text( 'Effect' ).setWidth( '90px' ) );
container.add( row );
var effects = editor.effects;
var options = {};
for ( var i = 0; i < effects.length; i ++ ) {
options[ i ] = effects[ i ].name;
}
var effectsSelect = new UI.Select().setWidth( '130px' );
effectsSelect.setOptions( options ).setValue( effects.indexOf( selected.effect ) );
effectsSelect.onChange( function () {
editor.timeline.reset();
selected.effect = editor.effects[ this.getValue() ];
signals.animationModified.dispatch( selected );
build();
} );
row.add( effectsSelect );
var edit = new UI.Button( 'EDIT' ).setMarginLeft( '8px' );
edit.onClick( function () {
editor.selectEffect( selected.effect );
} );
row.add( edit );
var row = new UI.Row();
row.add( new UI.Text( 'Name' ).setWidth( '90px' ) );
container.add( row );
var effectName = new UI.Input( selected.effect.name );
effectName.onChange( function () {
selected.effect.name = this.getValue();
signals.effectRenamed.dispatch( selected.effect );
} );
row.add( effectName );
//
var parameters = selected.effect.program.parameters;
for ( var key in parameters ) {
container.add( createParameterRow( key, parameters[ key ] ) );
}
}
//
signals.editorCleared.add( function () {
selected = null;
build();
} );
signals.animationSelected.add( function ( animation ) {
selected = animation;
build();
} );
signals.effectCompiled.add( build );
/*
signals.timeChanged.add( function () {
if ( selected !== null ) {
for ( var key in values ) {
values[ key ].setValue( selected.module.parameters[ key ].value );
}
}
} );
*/
return container;
};

View File

@ -0,0 +1,183 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Sidebar.Project = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setId( 'project' );
// Libraries
container.add( new UI.Text( 'Libraries' ).setTextTransform( 'uppercase' ) );
container.add( new UI.Break(), new UI.Break() );
var libraries = new UI.Select().setMultiple( true ).setWidth( '280px' );
container.add( libraries );
container.add( new UI.Break(), new UI.Break() );
// Effects
container.add( new UI.Text( 'Effects' ).setTextTransform( 'uppercase' ) );
container.add( new UI.Break(), new UI.Break() );
var effects = new UI.Select().setMultiple( true ).setWidth( '280px' ).setMarginBottom( '8px' );
container.add( effects );
var cleanEffects = new UI.Button( 'Clean Effects' );
cleanEffects.onClick( function () {
editor.cleanEffects();
} );
container.add( cleanEffects );
container.add( new UI.Break(), new UI.Break() );
// Scripts
container.add( new UI.Text( 'Scripts' ).setTextTransform( 'uppercase' ) );
container.add( new UI.Break(), new UI.Break() );
var includesContainer = new UI.Row();
container.add( includesContainer );
var newInclude = new UI.Button( 'New' );
newInclude.onClick( function () {
editor.addInclude( 'Name', '' );
update();
} );
container.add( newInclude );
var reload = new UI.Button( 'Reload Scripts' );
reload.onClick( function () {
editor.reloadIncludes();
var effects = editor.effects;
for ( var j = 0; j < effects.length; j++ ) {
var effect = effects[ j ];
editor.compileEffect( effect );
}
editor.timeline.reset();
editor.timeline.update( editor.player.currentTime );
} );
reload.setMarginLeft( '4px' );
container.add( reload );
container.add( new UI.Break(), new UI.Break() );
//
function buildInclude( id ) {
var include = editor.includes[ id ];
var span = new UI.Span();
var name = new UI.Input( include.name ).setWidth( '130px' ).setFontSize( '12px' );
name.onChange( function () {
include.name = this.getValue();
} );
span.add( name );
var edit = new UI.Button( 'Edit' );
edit.setMarginLeft( '4px' );
edit.onClick( function () {
editor.selectInclude( include );
} );
span.add( edit );
var remove = new UI.Button( 'Remove' );
remove.setMarginLeft( '4px' );
remove.onClick( function () {
if ( confirm( 'Are you sure?' ) ) {
editor.removeInclude( include );
}
} );
span.add( remove );
return span;
}
//
function update() {
updateLibraries();
updateEffects();
updateScripts();
}
function updateLibraries() {
libraries.setOptions( editor.libraries );
libraries.dom.size = editor.libraries.length;
}
function updateEffects() {
var names = [];
for ( var i = 0; i < editor.effects.length; i ++ ) {
names.push( editor.effects[ i ].name );
}
effects.setOptions( names );
effects.dom.size = editor.effects.length;
}
function updateScripts() {
includesContainer.clear();
var includes = editor.includes;
for ( var i = 0; i < includes.length; i ++ ) {
includesContainer.add( buildInclude( i ) );
}
}
// signals
signals.editorCleared.add( update );
signals.libraryAdded.add( updateLibraries );
signals.effectAdded.add( updateEffects );
signals.effectRemoved.add( updateEffects );
signals.includeAdded.add( updateScripts );
signals.includeRemoved.add( updateScripts );
return container;
};

View File

@ -0,0 +1,12 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Sidebar.Settings = function ( editor ) {
var container = new UI.Panel();
container.setId( 'settings' );
return container;
};

View File

@ -0,0 +1,80 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Sidebar = function ( editor ) {
var container = new UI.Panel();
container.setId( 'sidebar' );
//
var animationTab = new UI.Text( 'ANIMATION' ).onClick( onClick );
var projectTab = new UI.Text( 'PROJECT' ).onClick( onClick );
// var settingsTab = new UI.Text( 'SETTINGS' ).onClick( onClick );
var tabs = new UI.Div();
tabs.setId( 'tabs' );
tabs.add( animationTab, projectTab/*, settingsTab*/ );
container.add( tabs );
function onClick( event ) {
select( event.target.textContent );
}
//
var animation = new UI.Span().add(
new Sidebar.Animation( editor )
);
container.add( animation );
var project = new UI.Span().add(
new Sidebar.Project( editor )
);
container.add( project );
/*
var settings = new UI.Span().add(
new Sidebar.Settings( editor )
);
container.add( settings );
*/
//
function select( section ) {
animationTab.setClass( '' );
projectTab.setClass( '' );
// settingsTab.setClass( '' );
animation.setDisplay( 'none' );
project.setDisplay( 'none' );
// settings.setDisplay( 'none' );
switch ( section ) {
case 'ANIMATION':
animationTab.setClass( 'selected' );
animation.setDisplay( '' );
break;
case 'PROJECT':
projectTab.setClass( 'selected' );
project.setDisplay( '' );
break;
/*
case 'SETTINGS':
settingsTab.setClass( 'selected' );
settings.setDisplay( '' );
break;
*/
}
}
select( 'ANIMATION' );
return container;
};

View File

@ -0,0 +1,305 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Timeline.Animations = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
container.setHeight( '100%' );
container.setBackground( 'linear-gradient(#444 1px, transparent 1px) 0% 0% / 32px 32px repeat' );
var scale = 32;
var Block = ( function ( animation ) {
var scope = this;
var dom = document.createElement( 'div' );
dom.className = 'block';
dom.style.position = 'absolute';
dom.style.height = '30px';
dom.addEventListener( 'click', function ( event ) {
editor.selectAnimation( animation );
} );
dom.addEventListener( 'mousedown', function ( event ) {
var movementX = 0;
var movementY = 0;
function onMouseMove( event ) {
movementX = event.movementX | event.webkitMovementX | event.mozMovementX | 0;
animation.start += movementX / scale;
animation.end += movementX / scale;
if ( animation.start < 0 ) {
var offset = - animation.start;
animation.start += offset;
animation.end += offset;
}
movementY += event.movementY | event.webkitMovementY | event.mozMovementY | 0;
if ( movementY >= 30 ) {
animation.layer = animation.layer + 1;
movementY = 0;
}
if ( movementY <= -30 ) {
animation.layer = Math.max( 0, animation.layer - 1 );
movementY = 0;
}
signals.animationModified.dispatch( animation );
}
function onMouseUp( event ) {
document.removeEventListener( 'mousemove', onMouseMove );
document.removeEventListener( 'mouseup', onMouseUp );
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}, false );
var resizeLeft = document.createElement( 'div' );
resizeLeft.style.position = 'absolute';
resizeLeft.style.width = '6px';
resizeLeft.style.height = '30px';
resizeLeft.style.cursor = 'w-resize';
resizeLeft.addEventListener( 'mousedown', function ( event ) {
event.stopPropagation();
var movementX = 0;
function onMouseMove( event ) {
movementX = event.movementX | event.webkitMovementX | event.mozMovementX | 0;
animation.start += movementX / scale;
signals.animationModified.dispatch( animation );
}
function onMouseUp( event ) {
if ( Math.abs( movementX ) < 2 ) {
editor.selectAnimation( animation );
}
document.removeEventListener( 'mousemove', onMouseMove );
document.removeEventListener( 'mouseup', onMouseUp );
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}, false );
dom.appendChild( resizeLeft );
var name = document.createElement( 'div' );
name.className = 'name';
dom.appendChild( name );
var resizeRight = document.createElement( 'div' );
resizeRight.style.position = 'absolute';
resizeRight.style.right = '0px';
resizeRight.style.top = '0px';
resizeRight.style.width = '6px';
resizeRight.style.height = '30px';
resizeRight.style.cursor = 'e-resize';
resizeRight.addEventListener( 'mousedown', function ( event ) {
event.stopPropagation();
var movementX = 0;
function onMouseMove( event ) {
movementX = event.movementX | event.webkitMovementX | event.mozMovementX | 0;
animation.end += movementX / scale;
signals.animationModified.dispatch( animation );
}
function onMouseUp( event ) {
if ( Math.abs( movementX ) < 2 ) {
editor.selectAnimation( animation );
}
document.removeEventListener( 'mousemove', onMouseMove );
document.removeEventListener( 'mouseup', onMouseUp );
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}, false );
dom.appendChild( resizeRight );
//
function getAnimation() {
return animation;
}
function select() {
dom.classList.add( 'selected' );
}
function deselect() {
dom.classList.remove( 'selected' );
}
function update() {
animation.enabled === false ? dom.classList.add( 'disabled' ) : dom.classList.remove( 'disabled' );
dom.style.left = ( animation.start * scale ) + 'px';
dom.style.top = ( animation.layer * 32 ) + 'px';
dom.style.width = ( ( animation.end - animation.start ) * scale - 2 ) + 'px';
name.innerHTML = animation.name + ' <span style="opacity:0.5">' + animation.effect.name + '</span>';
}
update();
return {
dom: dom,
getAnimation: getAnimation,
select: select,
deselect: deselect,
update: update
};
} );
container.dom.addEventListener( 'dblclick', function ( event ) {
var start = event.offsetX / scale;
var end = start + 2;
var layer = Math.floor( event.offsetY / 32 );
var effect = new FRAME.Effect( 'Effect' );
editor.addEffect( effect );
var animation = new FRAME.Animation( 'Animation', start, end, layer, effect );
editor.addAnimation( animation );
} );
// signals
var blocks = {};
var selected = null;
signals.animationAdded.add( function ( animation ) {
var block = new Block( animation );
container.dom.appendChild( block.dom );
blocks[ animation.id ] = block;
} );
signals.animationModified.add( function ( animation ) {
blocks[ animation.id ].update();
} );
signals.animationSelected.add( function ( animation ) {
if ( blocks[ selected ] !== undefined ) {
blocks[ selected ].deselect();
}
if ( animation === null ) return;
selected = animation.id;
blocks[ selected ].select();
} );
signals.animationRemoved.add( function ( animation ) {
var block = blocks[ animation.id ];
container.dom.removeChild( block.dom );
delete blocks[ animation.id ];
} );
signals.timelineScaled.add( function ( value ) {
scale = value;
for ( var key in blocks ) {
blocks[ key ].update();
}
} );
signals.animationRenamed.add( function ( animation ) {
blocks[ animation.id ].update();
} );
signals.effectRenamed.add( function ( effect ) {
for ( var key in blocks ) {
var block = blocks[ key ];
if ( block.getAnimation().effect === effect ) {
block.update();
}
}
} );
return container;
};

View File

@ -0,0 +1,72 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
Timeline.Curves = function ( editor ) {
var signals = editor.signals;
var container = new UI.Panel();
var selected = null;
var scale = 32;
var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
svg.style.position = 'fixed';
svg.setAttribute( 'width', 2048 );
svg.setAttribute( 'height', 128 );
container.dom.appendChild( svg );
var path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
path.setAttribute( 'style', 'stroke: #444444; stroke-width: 1px; fill: none;' );
path.setAttribute( 'd', 'M 0 64 2048 65');
svg.appendChild( path );
var path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
path.setAttribute( 'style', 'stroke: #00ff00; stroke-width: 1px; fill: none;' );
svg.appendChild( path );
function drawCurve() {
/*
var curve = selected;
var drawing = '';
for ( var i = 0; i <= 2048; i ++ ) {
curve.update( i / scale );
drawing += ( i === 0 ? 'M' : 'L' ) + i + ' ' + ( ( 1 - curve.value ) * 64 ) + ' ';
}
path.setAttribute( 'd', drawing );
*/
}
// signals
signals.curveAdded.add( function ( curve ) {
if ( curve instanceof FRAME.Curves.Saw ) {
selected = curve;
drawCurve();
}
} );
signals.timelineScaled.add( function ( value ) {
scale = value;
drawCurve();
} );
return container;
};

View File

@ -0,0 +1,263 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Timeline = function ( editor ) {
var signals = editor.signals;
var player = editor.player;
var container = new UI.Panel();
container.setId( 'timeline' );
// controls
/*
var buttons = new UI.Div();
buttons.setPosition( 'absolute' );
buttons.setTop( '5px' );
buttons.setRight( '5px' );
controls.add( buttons );
var button = new UI.Button();
button.setLabel( 'ANIMATIONS' );
button.onClick( function () {
elements.setDisplay( '' );
curves.setDisplay( 'none' );
} );
buttons.add( button );
var button = new UI.Button();
button.setLabel( 'CURVES' );
button.setMarginLeft( '4px' );
button.onClick( function () {
scroller.style.background = '';
elements.setDisplay( 'none' );
curves.setDisplay( '' );
} );
buttons.add( button );
*/
// timeline
var keysDown = {};
document.addEventListener( 'keydown', function ( event ) { keysDown[ event.keyCode ] = true; } );
document.addEventListener( 'keyup', function ( event ) { keysDown[ event.keyCode ] = false; } );
var scale = 32;
var prevScale = scale;
var timeline = new UI.Panel();
timeline.setPosition( 'absolute' );
timeline.setTop( '0px' );
timeline.setBottom( '0px' );
timeline.setWidth( '100%' );
timeline.setOverflow( 'hidden' );
timeline.dom.addEventListener( 'wheel', function ( event ) {
if ( event.altKey === true ) {
event.preventDefault();
scale = Math.max( 2, scale + ( event.deltaY / 10 ) );
signals.timelineScaled.dispatch( scale );
}
} );
container.add( timeline );
var canvas = document.createElement( 'canvas' );
canvas.height = 32;
canvas.style.position = 'absolute';
canvas.addEventListener( 'mousedown', function ( event ) {
event.preventDefault();
function onMouseMove( event ) {
editor.setTime( ( event.offsetX + scroller.scrollLeft ) / scale );
}
function onMouseUp( event ) {
onMouseMove( event );
document.removeEventListener( 'mousemove', onMouseMove );
document.removeEventListener( 'mouseup', onMouseUp );
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}, false );
timeline.dom.appendChild( canvas );
function updateMarks() {
canvas.width = scroller.clientWidth;
var context = canvas.getContext( '2d', { alpha: false } );
context.fillStyle = '#555';
context.fillRect( 0, 0, canvas.width, canvas.height );
context.strokeStyle = '#888';
context.beginPath();
context.translate( - scroller.scrollLeft, 0 );
var duration = editor.duration;
var width = duration * scale;
var scale4 = scale / 4;
for ( var i = 0.5; i <= width; i += scale ) {
context.moveTo( i + ( scale4 * 0 ), 18 ); context.lineTo( i + ( scale4 * 0 ), 26 );
if ( scale > 16 ) context.moveTo( i + ( scale4 * 1 ), 22 ), context.lineTo( i + ( scale4 * 1 ), 26 );
if ( scale > 8 ) context.moveTo( i + ( scale4 * 2 ), 22 ), context.lineTo( i + ( scale4 * 2 ), 26 );
if ( scale > 16 ) context.moveTo( i + ( scale4 * 3 ), 22 ), context.lineTo( i + ( scale4 * 3 ), 26 );
}
context.stroke();
context.font = '10px Arial';
context.fillStyle = '#888'
context.textAlign = 'center';
var step = Math.max( 1, Math.floor( 64 / scale ) );
for ( var i = 0; i < duration; i += step ) {
var minute = Math.floor( i / 60 );
var second = Math.floor( i % 60 );
var text = ( minute > 0 ? minute + ':' : '' ) + ( '0' + second ).slice( - 2 );
context.fillText( text, i * scale, 13 );
}
}
var scroller = document.createElement( 'div' );
scroller.style.position = 'absolute';
scroller.style.top = '32px';
scroller.style.bottom = '0px';
scroller.style.width = '100%';
scroller.style.overflow = 'auto';
scroller.addEventListener( 'scroll', function ( event ) {
updateMarks();
updateTimeMark();
}, false );
timeline.dom.appendChild( scroller );
var elements = new Timeline.Animations( editor );
scroller.appendChild( elements.dom );
/*
var curves = new Timeline.Curves( editor );
curves.setDisplay( 'none' );
scroller.appendChild( curves.dom );
*/
function updateContainers() {
var width = editor.duration * scale;
elements.setWidth( width + 'px' );
// curves.setWidth( width + 'px' );
}
//
var loopMark = document.createElement( 'div' );
loopMark.style.position = 'absolute';
loopMark.style.top = 0;
loopMark.style.height = 100 + '%';
loopMark.style.width = 0;
loopMark.style.background = 'rgba( 255, 255, 255, 0.1 )';
loopMark.style.pointerEvents = 'none';
loopMark.style.display = 'none';
timeline.dom.appendChild( loopMark );
var timeMark = document.createElement( 'div' );
timeMark.style.position = 'absolute';
timeMark.style.top = '0px';
timeMark.style.left = '-8px';
timeMark.style.width = '16px';
timeMark.style.height = '100%';
timeMark.style.background = 'linear-gradient(90deg, transparent 8px, #f00 8px, #f00 9px, transparent 9px) 0% 0% / 16px 16px repeat-y';
timeMark.style.pointerEvents = 'none';
timeline.dom.appendChild( timeMark );
function updateTimeMark() {
timeMark.style.left = ( player.currentTime * scale ) - scroller.scrollLeft - 8 + 'px';
// TODO Optimise this
var loop = player.getLoop();
if ( Array.isArray( loop ) ) {
var loopStart = loop[ 0 ] * scale;
var loopEnd = loop[ 1 ] * scale;
loopMark.style.display = '';
loopMark.style.left = ( loopStart - scroller.scrollLeft ) + 'px';
loopMark.style.width = ( loopEnd - loopStart ) + 'px';
} else {
loopMark.style.display = 'none';
}
}
// signals
signals.timeChanged.add( function () {
updateTimeMark();
} );
signals.timelineScaled.add( function ( value ) {
scale = value;
scroller.scrollLeft = ( scroller.scrollLeft * value ) / prevScale;
updateMarks();
updateTimeMark();
updateContainers();
prevScale = value;
} );
signals.windowResized.add( function () {
updateMarks();
updateContainers();
} );
return container;
};

View File

@ -0,0 +1,43 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
var Viewport = function ( editor ) {
var scope = this;
var signals = editor.signals;
var container = this.container = new UI.Panel();
container.setId( 'viewport' );
editor.resources.set( 'dom', container.dom );
editor.signals.fullscreen.add( function () {
var element = container.dom.firstChild;
if ( element.requestFullscreen ) element.requestFullscreen();
if ( element.msRequestFullscreen ) element.msRequestFullscreen();
if ( element.mozRequestFullScreen ) element.mozRequestFullScreen();
if ( element.webkitRequestFullscreen ) element.webkitRequestFullscreen();
} );
function clear () {
var dom = container.dom;
while ( dom.children.length ) {
dom.removeChild( dom.lastChild );
}
}
signals.editorCleared.add( clear );
signals.includesCleared.add( clear );
return container;
};