Three.js 使一个可拖动的旋转车轮卡入均匀分布的位置

Three.js 使一个可拖动的旋转车轮卡入均匀分布的位置,three.js,tween.js,Three.js,Tween.js,我正在使用mrdoob的THREE.js在WebGL画布中渲染一个轮子 我想让轮子转动 围绕它的中心旋转 可通过鼠标或触摸交互拖动 通过施加假摩擦力来减速 每当旋转速度达到某个阈值时,捕捉到楔块的中心 你可以把轮子的行为想象成彩票轮子的行为 到目前为止,我已经取得了1-3分。这是我的代码: 'use strict'; var WIDTH = 1080, HEIGHT = 1080; var VIEW_ANGLE = 45, ASPECT = WIDTH / HEIGHT,

我正在使用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();
    
    “严格使用”;
    可变宽度=1080,
    高度=1080;
    var视图_角度=45,
    纵横比=宽度/高度,
    接近=0.1,
    FAR=10000;
    var摄像机=新的三视角摄像机(
    视角,
    方面,,
    近的
    远);
    var scene=new THREE.scene();
    场景。添加(摄影机);
    摄像机位置z=300;
    //创建渲染器
    var container=document.querySelector(“#test”);
    var renderer=new THREE.WebGLRenderer();
    设置大小(宽度、高度);
    renderer.setClearColor(0x000000,0);
    container.appendChild(renderer.domeElement);
    //创建对象
    var wheelMaterial=新的三网格基本材料({
    map:THREE.ImageUtils.loadTexture('wheel.png'),
    depthWrite:false,
    字母测试:0.5
    });
    wheelMaterial.overdraw=真;
    var车轮=新的三个网格(
    新的三平面几何(240240),
    车轮材料);
    场景。添加(轮子);
    //鼠标交互
    var IsDraging=错误;
    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);
    启动功能(e){
    IsDraging=true;
    console.log('drawing',e);
    mouseCoords=pageCoordsToCanvasCoords(e);
    旋转历史=[];
    }
    函数onDragEnd(e){
    IsDraging=错误;
    lastMouseCoords=null;
    mouseCoords=null;
    log('Drag end');
    }
    移动鼠标的功能(e){
    e、 预防默认值();
    mouseCoords=pageCoordsToCanvasCoords(e);
    }
    //效用函数
    功能页面CoordsToCanvasCoords(e){
    var canvasX;
    var游说;
    如果('touchs'在e&&e.touchs.length>0){
    canvasX=e.touchs[0].pageX;
    canvasY=e.touchs[0].pageY;
    }否则{
    canvasX=e.pageX
    拉票
    }
    canvasX-=e.target.offsetLeft;
    拉票-=e.target.offsetop;
    控制台日志(canvasX,canvasY);
    返回{
    x:画布,
    y:拉票
    };
    }
    功能鼠标旋转或存储旋转(x,y){
    var origoX=宽度/2.0,
    原点=高度/2.0;
    x=x-origoX;
    y=y-原点;
    var atan=数学上的atan2(x,y);
    返回atan;
    }
    函数getMeanVelocity(历史记录){
    
    如果(history.length我已经解决了这个问题

    'use strict';
    function Wheel (element, options) {
        var self = this;
    
        // Variable initialization
        var WIDTH = options.width;
        var HEIGHT = options.height;
    
        if (!options.image) {
            throw new Error('Image argument missing');
        }
    
        var image = options.image;
    
        var showStats = options.showStats || options.stats;
    
        // Core variables
        var stats;
        var wheel;
        var domElement;
        var scene;
        var camera;
        var renderer;
        var rotationHistory;
        var input;
        var animate;
        var run = false;
        var ROTATION_HISTORY_MAX_LENGTH = 5;
    
        switch (typeof element) {
            case 'string':
                domElement = document.querySelector(element);
                break;
            default:
                if ('className' in element) {
                    domElement = element;
                } else {
                    throw new Error('Invalid element: ', element);
                }
        }
    
        if (typeof element == 'undefined') {
            throw new Error('Invalid element.')
        }
    
        /* Initializes the WebGL canvas with the wheel plane */
        function setupScene() {
            var VIEW_ANGLE = 45,
                ASPECT = WIDTH / HEIGHT,
                NEAR = 0.1,
                FAR = 10000;
    
            camera = new THREE.PerspectiveCamera(
                VIEW_ANGLE,
                ASPECT,
                NEAR,
                FAR);
    
            scene = new THREE.Scene();
    
            scene.add(camera);
    
            camera.position.z = 300;
    
            // Create renderer
    
            var container = domElement;
    
            renderer = new THREE.WebGLRenderer();
            renderer.setSize(WIDTH * 2, HEIGHT * 2);
            renderer.setClearColor(0x000000, 0);
    
            // Create objects
    
            var wheelMaterial = new THREE.MeshBasicMaterial({
                map: THREE.ImageUtils.loadTexture(image),
                depthWrite: false,
                alphaTest: 0.5
            });
    
            wheelMaterial.overdraw = true;
    
            wheel = new THREE.Mesh(
                new THREE.PlaneGeometry(245, 245),
                wheelMaterial);
    
            scene.add(wheel);
    
            container.appendChild(renderer.domElement);
        }
    
        function setupStats() {
            // Init stats
            stats = new Stats();
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.top = '0px';
            document.body.appendChild(stats.domElement);
        }
    
        function setup() {
            setupScene();
            if (showStats) {
                setupStats();
            }
        }
    
        setup();
    
        // The tick function
        function update() {
    
            animate.update(); // Process interactions
    
            self.renderer.render(self.scene, self.camera);
    
            if (showStats) {
                self.stats.update();
            }
            if (run) {
                window.requestAnimationFrame(update);
            }
        }
    
        animate = new Animate();
    
        // Start and run the wheel every animationframe
        function start() {
            self.input = input = new Input();  // Start capturing input
            run = true;
            update();
        }
    
        /**
         * Animate the wheel
         */
        function Animate() {
            var self = this;
    
            self.velocity = 0;
    
            var velocityPositive = 0;
    
            self.friction = 0.001;
    
            self.snapThreshold = 0.03;
    
            self.isSnapping = false;
    
            var lastAnimationTime;
    
            self.tween;
    
            var rotationHistory = [];
    
            var PARTITIONS = 12;
            var PARTITION_ARC = 1 * Math.PI / (PARTITIONS / 2); // The width of each section
    
            function update() {
                var currentTime = (new Date).getTime();
    
                velocityPositive = self.velocity;
    
                if (velocityPositive < 0) {
                    velocityPositive /= -1;
                }
    
                if (!self.isSnapping
                    && !input.isDragging
                    && velocityPositive < self.snapThreshold
                    && velocityPositive > 0
                    && lastAnimationTime) {
                    rotationHistory = [];
                    snap();
                }
    
                if (input.isDragging) {
                    self.isSnapping = false;
                    TWEEN.removeAll();
                }
    
                if (!self.isSnapping) {
                    /**
                     * If the mouse is dragging the wheel
                     */
                    if (input.lastMouseCoords && input.isDragging && !input.isSnapping) {
                        // Reset the velocity for the slowdown
                        self.velocity = 0;
    
                        // Get the delta rotation since last mouse coordinates
                        var deltaRadians = input.mouseCoordsToRadian(
                            input.mouseCoords.x, input.mouseCoords.y)
                            - input.mouseCoordsToRadian(
                                input.lastMouseCoords.x,
                                input.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 (input.isDragging) {
                        input.lastMouseCoords = input.mouseCoords;
                    }
                    // Continue rotation of the released wheel
                    if (!input.isDragging
                        && !input.lastMouseCoords
                        && lastAnimationTime
                        && !self.isSnapping) {
    
                        var delta = currentTime - lastAnimationTime;
    
                        if (self.velocity == 0) {
                            var meanVelocityOverTime = getMeanVelocity(rotationHistory);
    
                            self.velocity = meanVelocityOverTime;
                        } else if (!self.isSnapping && !self.isDragging) {
                            var currentIsNegative = self.velocity < 0 ? true : false;
    
                            var currentVelocity = self.velocity;
    
                            if (currentIsNegative) {
                                currentVelocity /= -1;
                            }
    
                            var newVelocity = applyFakeFriction(currentVelocity,
                                                                delta);
    
                            if (newVelocity < 0) {
                                self.velocity = 0;
                                rotationHistory = [];
                            } else {
                                if (currentIsNegative) {
                                    // Revert to old polarity
                                    newVelocity /= -1;
                                }
                                self.velocity = newVelocity;
                            }
                        }
    
                        wheel.rotation.z += self.velocity * delta;
    
                    }
    
                    if (!self.isSnapping) {
                        while (wheel.rotation.z > 2 * Math.PI) {
                            wheel.rotation.z -= 2 * Math.PI;
                        }
    
                        while (wheel.rotation.z < - (2 * Math.PI)) {
                            wheel.rotation.z += 2 * Math.PI;
                        }
                    }
                }
    
                // Update snap tween
                TWEEN.update();
    
                // Update the history record
                lastAnimationTime = currentTime;
            }
    
    
            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 -= self.friction; // * (10000 * wheelSlowDownVelocityPositive); // * (nearestBorderDistance * BORDER_FRICTION);
                }
                return velocity;
            }
    
    
            function getNearestWedge() {
                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;
                        }
                    }
                }
    
                return {
                    position: nearestBorder,
                    distance: nearestBorderDistance
                };
            }
    
            function snap() {
                console.log('Snapping');
                if (self.isSnapping) {
                    console.error('Already snapping, aborting.');
                    return;
                }
    
                self.isSnapping = true;
                self.velocity = 0;
                var nearest = getNearestWedge();
    
                TWEEN.removeAll();
    
                console.log('nearest: ', nearest.position, nearest.distance)
    
                self.tween = new TWEEN.Tween({r: wheel.rotation.z})
                    .to({r: nearest.position})
                    .easing(TWEEN.Easing.Elastic.Out)
                    .onUpdate(onUpdate)
                    .onComplete(onComplete)
                    .start();
    
                function onUpdate() {
                    //console.log('current: ', this.r, self.velocity);
                    wheel.rotation.z = this.r;
                };
    
                function onComplete() {
                    self.isSnapping = false;
                    console.log('Not snapping');;
                }
    
            }
    
            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;
            }
    
            // Internal utilities
            function log() {
                if (console && _log) {
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift('Animate: ')
                    console.log.apply(console, args);
                }
            }
    
            // exports
            this.update = update;
            this.rotationHistory = rotationHistory;
            this.PARTITIONS = PARTITIONS;
            this.PARTITION_ARC = PARTITION_ARC;
            this.snap = snap;
    
            return this;
        }
    
    
        /**
         * Handles input to the wheel.
         */
        function Input() {
            var self = this;
            var _log = true;
    
            domElement.addEventListener('mousedown', onDragStart, false);
            //domElement.addEventListener('touchstart', onDragStart, false);
    
            domElement.addEventListener('mouseup', onDragEnd, false);
            domElement.addEventListener('mouseout', onDragEnd, false);
            //domElement.addEventListener('touchend', onDragEnd, false);
    
            domElement.addEventListener('mousemove', onMouseMove, false);
            //domElement.addEventListener('touchmove', onMouseMove, false);
    
            function onDragStart(e) {
                self.isDragging = true;
                log('Drag start');
                self.mouseCoords = pageCoordsToCanvasCoords(e);
                animate.rotationHistory = [];
            }
    
            function onDragEnd(e) {
                self.isDragging = false;
                self.lastMouseCoords = null;
                self.mouseCoords = null;
                log('Drag end');
            }
    
            function onMouseMove(e) {
                e.preventDefault();
                self.mouseCoords = pageCoordsToCanvasCoords(e);
            }
    
            function pageCoordsToCanvasCoords(e) {
                var canvasX, 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 mouseCoordsToRadian(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;
            }
    
            // exports
            this.mouseCoordsToRadian = mouseCoordsToRadian;
    
            function log() {
                if (console && _log) {
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift('Input: ')
                    console.log.apply(console, args);
                }
            }
            return this;
        }
    
        // Internal utils
        function log() {
            if (console && _log) {
                var args = Array.prototype.slice.call(arguments, 0);
                args.unshift('Wheel: ')
                console.log.apply(console, args);
            }
        }
    
        // exports
        self.start = start;
        self.update = update;
        self.scene = scene;
        self.camera = camera;
        self.wheel = wheel;
        self.renderer = renderer;
        self.stats = stats;
        self.domElement = domElement;
        self.input = input;
        self.animate = animate;
    
        return self;
    }
    
    “严格使用”;
    功能控制盘(元件、选项){
    var self=这个;
    //变量初始化
    变量宽度=options.WIDTH;
    var HEIGHT=options.HEIGHT;
    如果(!options.image){
    抛出新错误(“缺少图像参数”);
    }
    var image=options.image;
    var showStats=options.showStats | | options.stats;
    //核心变量
    var统计;
    var轮;
    变分元素;
    var场景;
    var摄像机;
    var渲染器;
    var旋转历史;
    var输入;
    变量动画;
    var-run=false;
    变量旋转历史最大长度=5;
    开关(元件类型){
    大小写“string”:
    DOMELENT=文档查询选择器(元素);
    打破
    违约:
    if(元素中的('className')){
    DOMELENT=元素;
    }否则{
    抛出新错误(“无效元素:”,元素);
    }
    }
    if(元素类型=='undefined'){
    抛出新错误('无效元素')
    }
    /*使用控制盘平面初始化WebGL画布*/
    函数setupScene(){
    var视图_角度=45,
    纵横比=宽度/高度,
    接近=0.1,
    FAR=10000;
    摄像机=新的三视角摄像机(
    视角,
    方面,,
    近的
    远);
    场景=新的三个。场景();
    场景。添加(摄影机);
    摄像机位置z=300;
    //创建渲染器
    var容器=DOMELENT;
    renderer=new THREE.WebGLRenderer();
    设置大小(宽度*2,高度*2);
    renderer.setClearColor(0x000000,0);
    //创建对象
    var wheelMaterial=新的三网格基本材料({
    映射:三个.ImageUtils.loadTexture(图像),
    depthWrite:false,
    字母测试:0.5
    });
    wheelMaterial.overdraw=真;
    轮子=新的三个网格(
    新三.平面几何学(245245),
    车轮材料);
    场景。添加(轮子);
    container.appendChild(renderer.domeElement);
    }
    函数setupStats(){
    //初始统计
    统计数据=新统计数据();
    stats.domElement.style.position='绝对';
    stats.domElement.style.top='0px';
    document.body.appendChild(stats.domeElement);
    }
    函数设置(){
    设置场景();
    if(showStats){
    setupStats();
    }
    }
    设置();
    //滴答函数
    函数更新(){
    animate.update();//处理交互
    self.renderer.render(self.scene,self.camera);
    if(showStats){
    self.stats.update();
    }
    如果(运行){
    window.requestAnimationFrame(更新);
    }
    }
    动画=新动画();
    //启动并在每个动画帧运行控制盘
    函数start(){
    self.input=input=new input();//开始捕获输入
    run=true;
    更新();
    }
    /**
    *为轮子设置动画