kopia lustrzana https://github.com/robhawkes/vizicities
Added basic animated camera movement
rodzic
9882b8636d
commit
42bf0c4518
Plik diff jest za duży
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -41,43 +41,43 @@ var imageTileLayer = VIZI.imageTileLayer('http://{s}.basemaps.cartocdn.com/light
|
|||
// }).addTo(world);
|
||||
|
||||
// Building and roads from Mapzen (polygons and linestrings)
|
||||
var topoJSONTileLayer = VIZI.topoJSONTileLayer('https://vector.mapzen.com/osm/buildings,roads/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
|
||||
interactive: false,
|
||||
style: function(feature) {
|
||||
var height;
|
||||
|
||||
if (feature.properties.height) {
|
||||
height = feature.properties.height;
|
||||
} else {
|
||||
height = 10 + Math.random() * 10;
|
||||
}
|
||||
|
||||
return {
|
||||
height: height,
|
||||
lineColor: '#f7c616',
|
||||
lineWidth: 1,
|
||||
lineTransparent: true,
|
||||
lineOpacity: 0.2,
|
||||
lineBlending: THREE.AdditiveBlending,
|
||||
lineRenderOrder: 2
|
||||
};
|
||||
},
|
||||
filter: function(feature) {
|
||||
// Don't show points
|
||||
return feature.geometry.type !== 'Point';
|
||||
},
|
||||
// onEachFeature: function(feature, layer) {
|
||||
// if (feature.geometry.type !== 'Polygon') {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Make polygons clickable
|
||||
// layer.on('click', function(layer, point2d, point3d, intersects) {
|
||||
// console.log(layer, point2d, point3d, intersects);
|
||||
// });
|
||||
// },
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
|
||||
}).addTo(world);
|
||||
// var topoJSONTileLayer = VIZI.topoJSONTileLayer('https://vector.mapzen.com/osm/buildings,roads/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
|
||||
// interactive: false,
|
||||
// style: function(feature) {
|
||||
// var height;
|
||||
//
|
||||
// if (feature.properties.height) {
|
||||
// height = feature.properties.height;
|
||||
// } else {
|
||||
// height = 10 + Math.random() * 10;
|
||||
// }
|
||||
//
|
||||
// return {
|
||||
// height: height,
|
||||
// lineColor: '#f7c616',
|
||||
// lineWidth: 1,
|
||||
// lineTransparent: true,
|
||||
// lineOpacity: 0.2,
|
||||
// lineBlending: THREE.AdditiveBlending,
|
||||
// lineRenderOrder: 2
|
||||
// };
|
||||
// },
|
||||
// filter: function(feature) {
|
||||
// // Don't show points
|
||||
// return feature.geometry.type !== 'Point';
|
||||
// },
|
||||
// // onEachFeature: function(feature, layer) {
|
||||
// // if (feature.geometry.type !== 'Polygon') {
|
||||
// // return;
|
||||
// // }
|
||||
// //
|
||||
// // // Make polygons clickable
|
||||
// // layer.on('click', function(layer, point2d, point3d, intersects) {
|
||||
// // console.log(layer, point2d, point3d, intersects);
|
||||
// // });
|
||||
// // },
|
||||
// attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
|
||||
// }).addTo(world);
|
||||
|
||||
// Just buildings from Mapzen (polygons)
|
||||
// var topoJSONTileLayer = VIZI.topoJSONTileLayer('https://vector.mapzen.com/osm/buildings/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
|
||||
|
@ -104,30 +104,30 @@ var topoJSONTileLayer = VIZI.topoJSONTileLayer('https://vector.mapzen.com/osm/bu
|
|||
// attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
|
||||
// }).addTo(world);
|
||||
|
||||
// // London Underground lines
|
||||
// var geoJSONLayer = VIZI.geoJSONLayer('https://rawgit.com/robhawkes/4acb9d6a6a5f00a377e2/raw/30ae704a44e10f2e13fb7e956e80c3b22e8e7e81/tfl_lines.json', {
|
||||
// output: true,
|
||||
// interactive: true,
|
||||
// style: function(feature) {
|
||||
// var colour = feature.properties.lines[0].colour || '#ffffff';
|
||||
//
|
||||
// return {
|
||||
// lineColor: colour,
|
||||
// // lineHeight: 20,
|
||||
// lineWidth: 3,
|
||||
// // lineTransparent: true,
|
||||
// // lineOpacity: 0.5,
|
||||
// // lineBlending: THREE.AdditiveBlending,
|
||||
// lineRenderOrder: 2
|
||||
// };
|
||||
// },
|
||||
// onEachFeature: function(feature, layer) {
|
||||
// layer.on('click', function(layer, point2d, point3d, intersects) {
|
||||
// console.log(layer, point2d, point3d, intersects);
|
||||
// });
|
||||
// },
|
||||
// attribution: '© Transport for London.'
|
||||
// }).addTo(world);
|
||||
// London Underground lines
|
||||
var geoJSONLayer = VIZI.geoJSONLayer('https://rawgit.com/robhawkes/4acb9d6a6a5f00a377e2/raw/30ae704a44e10f2e13fb7e956e80c3b22e8e7e81/tfl_lines.json', {
|
||||
output: true,
|
||||
interactive: true,
|
||||
style: function(feature) {
|
||||
var colour = feature.properties.lines[0].colour || '#ffffff';
|
||||
|
||||
return {
|
||||
lineColor: colour,
|
||||
lineHeight: 20,
|
||||
lineWidth: 3,
|
||||
lineTransparent: true,
|
||||
lineOpacity: 0.5,
|
||||
lineBlending: THREE.AdditiveBlending,
|
||||
lineRenderOrder: 2
|
||||
};
|
||||
},
|
||||
onEachFeature: function(feature, layer) {
|
||||
layer.on('click', function(layer, point2d, point3d, intersects) {
|
||||
console.log(layer, point2d, point3d, intersects);
|
||||
});
|
||||
},
|
||||
attribution: '© Transport for London.'
|
||||
}).addTo(world);
|
||||
|
||||
// Set up render debug stats
|
||||
var rendererStats = new THREEx.RendererStats();
|
||||
|
|
|
@ -79,6 +79,14 @@ function build() {
|
|||
// Proxy the global proj4 variable to require('proj4')
|
||||
'proj4': 'proj4'
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'TweenLite': __dirname + '/node_modules/gsap/src/uncompressed/TweenLite.js',
|
||||
'TweenMax': __dirname + '/node_modules/gsap/src/uncompressed/TweenMax.js',
|
||||
'TimelineLite': __dirname + '/node_modules/gsap/src/uncompressed/TimelineLite.js',
|
||||
'TimelineMax': __dirname + '/node_modules/gsap/src/uncompressed/TimelineMax.js'
|
||||
}
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
"earcut": "^2.0.8",
|
||||
"eventemitter3": "^1.1.1",
|
||||
"geojson-merge": "^0.1.0",
|
||||
"gsap": "^1.18.2",
|
||||
"hammerjs": "^2.0.6",
|
||||
"lodash.assign": "^4.0.2",
|
||||
"lodash.throttle": "^4.0.0",
|
||||
|
|
|
@ -105,7 +105,7 @@ class World extends EventEmitter {
|
|||
|
||||
// Update controls
|
||||
this._controls.forEach(controls => {
|
||||
controls.update();
|
||||
controls.update(delta);
|
||||
});
|
||||
|
||||
this.emit('preUpdate', delta);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import EventEmitter from 'eventemitter3';
|
||||
import THREE from 'three';
|
||||
import OrbitControls from '../vendor/OrbitControls';
|
||||
import TweenLite from 'TweenLite';
|
||||
|
||||
// Prevent animation from pausing when tab is inactive
|
||||
TweenLite.lagSmoothing(0);
|
||||
|
||||
class Orbit extends EventEmitter {
|
||||
constructor() {
|
||||
|
@ -25,38 +29,127 @@ class Orbit extends EventEmitter {
|
|||
}
|
||||
|
||||
// Moving the camera along the [x,y,z] axis based on a target position
|
||||
_panTo(point, animate) {}
|
||||
_panBy(pointDelta, animate) {}
|
||||
panTo(point, animate) {}
|
||||
panBy(pointDelta, animate) {}
|
||||
|
||||
// Zooming the camera in and out
|
||||
_zoomTo(metres, animate) {}
|
||||
_zoomBy(metresDelta, animate) {}
|
||||
zoomTo(metres, animate) {}
|
||||
zoomBy(metresDelta, animate) {}
|
||||
|
||||
// Force camera to look at something other than the target
|
||||
_lookAt(point, animate) {}
|
||||
lookAt(point, animate) {}
|
||||
|
||||
// Make camera look at the target
|
||||
_lookAtTarget() {}
|
||||
lookAtTarget() {}
|
||||
|
||||
// Tilt (up and down)
|
||||
_tiltTo(angle, animate) {}
|
||||
_tiltBy(angleDelta, animate) {}
|
||||
tiltTo(angle, animate) {}
|
||||
tiltBy(angleDelta, animate) {}
|
||||
|
||||
// Rotate (left and right)
|
||||
_rotateTo(angle, animate) {}
|
||||
_rotateBy(angleDelta, animate) {}
|
||||
rotateTo(angle, animate) {}
|
||||
rotateBy(angleDelta, animate) {}
|
||||
|
||||
// Fly to the given point, animating pan and tilt/rotation to final position
|
||||
// with nice zoom out and in
|
||||
//
|
||||
// Calling flyTo a second time before the previous animation has completed
|
||||
// will immediately start the new animation from wherever the previous one
|
||||
// has got to
|
||||
_flyTo(point, noZoom) {}
|
||||
// TODO: Calling flyTo a second time before the previous animation has
|
||||
// completed should immediately start the new animation from wherever the
|
||||
// previous one has got to
|
||||
//
|
||||
// TODO: Long-distance pans should prevent the quadtree grid from trying to
|
||||
// update by not firing the control update events every frame until the
|
||||
// pan velocity calms down a bit
|
||||
flyToPoint(point, duration, noZoom) {
|
||||
// Animation time in seconds
|
||||
var animationTime = duration || 3;
|
||||
|
||||
this._flyTarget = new THREE.Vector3(point.x, 0, point.y);
|
||||
|
||||
// Calculate delta from current position to fly target
|
||||
var diff = new THREE.Vector3().subVectors(this._controls.target, this._flyTarget);
|
||||
|
||||
this._flyTween = new TweenLite(
|
||||
{
|
||||
x: 0,
|
||||
z: 0,
|
||||
prev: {
|
||||
x: 0,
|
||||
z: 0
|
||||
}
|
||||
},
|
||||
animationTime,
|
||||
{
|
||||
x: diff.x,
|
||||
z: diff.z,
|
||||
onUpdate: function(tween) {
|
||||
var controls = this._controls;
|
||||
|
||||
// Work out difference since last frame
|
||||
var deltaX = tween.target.x - tween.target.prev.x;
|
||||
var deltaZ = tween.target.z - tween.target.prev.z;
|
||||
|
||||
// Move some fraction toward the target point
|
||||
controls.panLeft(deltaX, controls.object.matrix);
|
||||
controls.panUp(deltaZ, controls.object.matrix);
|
||||
|
||||
tween.target.prev.x = tween.target.x;
|
||||
tween.target.prev.z = tween.target.z;
|
||||
|
||||
// TODO: Get zoom to dolly in and out on pan
|
||||
// controls.object.zoom += Math.sin(Math.PI / tween.target.zoom);
|
||||
// controls.object.updateProjectionMatrix();
|
||||
},
|
||||
onComplete: function(tween) {
|
||||
console.log(`Arrived at flyTarget`);
|
||||
this._flyTarget = null;
|
||||
},
|
||||
onUpdateParams: ['{self}'],
|
||||
onCompleteParams: ['{self}'],
|
||||
callbackScope: this,
|
||||
ease: Power1.easeInOut
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
flyToLatLon(latlon, duration, noZoom) {
|
||||
var point = this._world.latLonToPoint(latlon);
|
||||
this.flyToPoint(point, duration, noZoom);
|
||||
}
|
||||
|
||||
// TODO: Make this animate over a user-defined period of time
|
||||
//
|
||||
// Perhaps use TweenMax for now and implement as a more lightweight solution
|
||||
// later on once it all works
|
||||
// _animateFlyTo(delta) {
|
||||
// var controls = this._controls;
|
||||
//
|
||||
// // this._controls.panLeft(50, controls._controls.object.matrix);
|
||||
// // this._controls.panUp(50, controls._controls.object.matrix);
|
||||
// // this._controls.dollyIn(this._controls.getZoomScale());
|
||||
// // this._controls.dollyOut(this._controls.getZoomScale());
|
||||
//
|
||||
// // Calculate delta from current position to fly target
|
||||
// var diff = new THREE.Vector3().subVectors(this._controls.target, this._flyTarget);
|
||||
//
|
||||
// // 1000 units per second
|
||||
// var speed = 1000 * (delta / 1000);
|
||||
//
|
||||
// // Remove fly target after arrival and snap to target
|
||||
// if (diff.length() < 0.01) {
|
||||
// console.log(`Arrived at flyTarget`);
|
||||
// this._flyTarget = null;
|
||||
// speed = 1;
|
||||
// }
|
||||
//
|
||||
// // Move some fraction toward the target point
|
||||
// controls.panLeft(diff.x * speed, controls.object.matrix);
|
||||
// controls.panUp(diff.z * speed, controls.object.matrix);
|
||||
// }
|
||||
|
||||
// Proxy to OrbitControls.update()
|
||||
update() {
|
||||
this._controls.update();
|
||||
update(delta) {
|
||||
this._controls.update(delta);
|
||||
}
|
||||
|
||||
// Add controls to world instance and store world reference
|
||||
|
|
|
@ -1104,6 +1104,16 @@ var OrbitControls = function ( object, domElement ) {
|
|||
|
||||
window.addEventListener( 'keydown', onKeyDown, false );
|
||||
|
||||
// Expose controls methods for programmatic control
|
||||
this.panLeft = panLeft;
|
||||
this.panUp = panUp;
|
||||
this.pan = pan;
|
||||
this.dollyIn = dollyIn;
|
||||
this.dollyOut = dollyOut;
|
||||
this.getZoomScale = getZoomScale;
|
||||
this.rotateLeft = rotateLeft;
|
||||
this.rotateUp = rotateUp;
|
||||
|
||||
// force an update at start
|
||||
|
||||
this.update();
|
||||
|
|
Ładowanie…
Reference in New Issue