我正在使用 mrdoob 的 THREE.js 在 WebGL 画布中渲染一个轮子。
我想让轮子
- 围绕它的中心旋转
- 可通过鼠标或触摸交互拖动
- 通过施加假摩擦减速
- 每当旋转速度达到某个阈值时,捕捉到楔形的中心。
您可能会将轮子的行为视为彩票轮的行为。
到目前为止,我已经达到了 1-3 点。这是我的代码:
'use strict';
var WIDTH = 1080,
HEIGHT = 1080;
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
var scene = new THREE.Scene();
scene.add(camera);
camera.position.z = 300;
// Create renderer
var container = document.querySelector('#test');
var renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH, HEIGHT);
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);
// Create objects
var wheelMaterial = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('wheel.png'),
depthWrite: false,
alphaTest: 0.5
});
wheelMaterial.overdraw = true;
var wheel = new THREE.Mesh(
new THREE.PlaneGeometry(240, 240),
wheelMaterial);
scene.add(wheel);
// Mouse interaction
var isDragging = false;
var lastMouseCoords = null;
var mouseCoords = null;
container.addEventListener('mousedown', onDragStart, false);
container.addEventListener('touchstart', onDragStart, false);
container.addEventListener('mouseup', onDragEnd, false);
container.addEventListener('mouseout', onDragEnd, false);
container.addEventListener('touchend', onDragEnd, false);
container.addEventListener('mousemove', onMouseMove, false);
container.addEventListener('touchmove', onMouseMove, false);
function onDragStart(e) {
isDragging = true;
console.log('Dragging', e);
mouseCoords = pageCoordsToCanvasCoords(e);
rotationHistory = [];
}
function onDragEnd(e) {
isDragging = false;
lastMouseCoords = null;
mouseCoords = null;
console.log('Drag end');
}
function onMouseMove(e) {
e.preventDefault();
mouseCoords = pageCoordsToCanvasCoords(e);
}
// Utility functions
function pageCoordsToCanvasCoords(e) {
var canvasX;
var canvasY;
if ('touches' in e && e.touches.length > 0) {
canvasX = e.touches[0].pageX;
canvasY = e.touches[0].pageY;
} else {
canvasX = e.pageX
canvasY = e.pageY
}
canvasX -= e.target.offsetLeft;
canvasY -= e.target.offsetTop;
console.log(canvasX, canvasY);
return {
x: canvasX,
y: canvasY
};
}
function mouseCoordsToRotation(x, y) {
var origoX = WIDTH / 2.0,
origoY = HEIGHT / 2.0;
x = x - origoX;
y = y - origoY;
var atan = Math.atan2(x, y);
return atan;
}
function getMeanVelocity(history) {
if (history.length <= 1) {
return 0;
}
var movements = [];
var startTime = history[0].time;
var totalTimeElapsed = 0;
// Start from the second item in deltaRadians
for (var i = 1; i < history.length; i++) {
var item = history[i];
var movement = item.deltaRad;
movements.push(item.deltaRad);
var movementTimeDelta = item.time - startTime - totalTimeElapsed;
if (movementTimeDelta < 0) {
console.error('movementTimeDelta for history entry #' +
i + ' has travelled back in time somehow.');
}
totalTimeElapsed += movementTimeDelta;
}
var sum = movements.reduce(function (a, b) {
return a + b;
});
return sum / totalTimeElapsed;
}
function applyFakeFriction(velocity, time) {
/*
var currentRotation = wheel.rotation.z;
var nearestBorder = 0;
var nearestBorderDistance = 100;
for (var i = 0; i < PARTITIONS; i++) {
var partition = PARTITION_ARC * i - PARTITION_ARC * PARTITIONS / 2;
var distance = currentRotation - partition;
if (distance < 0) {
distance /= -1;
}
if (distance < nearestBorderDistance) {
console.log('distance is less than nearestBorderDistance')
nearestBorder = partition;
nearestBorderDistance = distance;
if (nearestBorderDistance < 0) {
nearestBorderDistance /= -1;
}
}
}
console.log('nearestBorderDistance: ', nearestBorderDistance);
*/
for (var i = 0; i < time; i++) {
velocity -= WHEEL_FRICTION; // * (nearestBorderDistance * BORDER_FRICTION);
}
return velocity;
}
var rotation = 1;
function snap() {
isSnapping = true;
/* Disabled, this the issue I'm asking about in the post
var update = function () {
cube.position.rotation = current.rotation;
}
var current = {
rotation: rotation
};
TWEEN.removeAll();
var easing = TWEEN.Easing['Elastic']['EaseInOut'];
var tweenHead = neww TWEEN.Tween(current)
.to({rotation: rotation})
.easing(easing)
.onUpdate(update);
tweenHead.start();
*/
}
var rotationHistory = []
var ROTATION_HISTORY_MAX_LENGTH = 5;
var WHEEL_FRICTION = 0.000001;
var BORDER_FRICTION = 2;
var PARTITIONS = 12;
var PARTITION_ARC = 1 * Math.PI / (PARTITIONS / 2); // The width of each section
var wheelVelocity = 0.1;
var wheelSlowDownVelocity = 0;
var lastWheelRotationTime;
var isSnapping = false;
// Render
function tick() {
// Rotate wheel
var currentTime = (new Date).getTime();
if (lastMouseCoords && isDragging) {
// Reset the velocity for the slowdown
wheelSlowDownVelocity = 0;
// Get the delta rotation since last mouse coordinates
var deltaRadians = mouseCoordsToRotation(mouseCoords.x, mouseCoords.y)
- mouseCoordsToRotation(lastMouseCoords.x, lastMouseCoords.y);
// Set the wheel rotation
wheel.rotation.z += deltaRadians;
// Save the rotation in the history and remove any excessive elements
rotationHistory.push({
time: currentTime,
deltaRad: deltaRadians
});
while (rotationHistory.length > ROTATION_HISTORY_MAX_LENGTH) {
rotationHistory.shift();
}
}
if (isDragging) {
lastMouseCoords = mouseCoords;
}
// Continue rotation of the released wheel
if (!isDragging && !lastMouseCoords && lastWheelRotationTime) {
var delta = currentTime - lastWheelRotationTime;
if (wheelSlowDownVelocity == 0) {
var meanVelocityOverTime = getMeanVelocity(rotationHistory);
wheelSlowDownVelocity = meanVelocityOverTime;
} else {
var currentIsNegative = wheelSlowDownVelocity < 0 ? true : false;
var currentVelocity = wheelSlowDownVelocity;
if (currentIsNegative) {
currentVelocity /= -1;
}
console.log('Current velocity: ', currentVelocity);
console.log('delta: ', delta);
var newVelocity = applyFakeFriction(currentVelocity,
delta);
console.log('New velocity: ', newVelocity);
if (newVelocity < 0) {
wheelSlowDownVelocity = 0;
rotationHistory = [];
} else {
if (currentIsNegative) {
// Revert to old polarity
newVelocity /= -1;
}
wheelSlowDownVelocity = newVelocity;
}
}
wheel.rotation.z += wheelSlowDownVelocity * delta;
}
while (wheel.rotation.z > 2 * Math.PI) {
console.log('Correcting rotation: ', wheel.rotation.z);
wheel.rotation.z -= 2 * Math.PI;
}
while (wheel.rotation.z < - (2 * Math.PI)) {
console.log('Correcting rotation: ', wheel.rotation.z);
wheel.rotation.z += 2 * Math.PI;
}
// Update the history record
lastWheelRotationTime = currentTime;
// Render scene and attach render callback to next animation frame.
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
}
tick();
我有完整的代码,在https://gist.github.com/joar/5747498上减去 wheel.png 。
我一直在寻找这种行为的例子,但到目前为止我还没有找到任何例子。
请注意编辑。请不要更改此帖子的标签。tween.js补间 !=
JS。