最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - CSS 3D animated wheel off center - Stack Overflow

programmeradmin0浏览0评论

I have a 3D css wheel/cylindar animation that's rotating on the x axis. My issue is the animation appears to move up and down outside of it's container. Example GIF below...

The code for the above can be found here here: /

(function($) {

  const $wheel = $('.wheel .wheel__inner');
  const items = 28;
  const diameter = $wheel.height();
  const radius = diameter / 2;
  const angle = 360 / items;
  const circumference = Math.PI * diameter;
  const height = circumference / items;

  for (let i = 0; i < items; i++) {
    var transform = `rotateX(${ angle * i }deg) translateZ(${ radius }px)`;

    $('<div>', {
      class: 'wheel__segment'
    }).css({
      'transform': transform,
      'height': height,
    }).html(`<span>Item ${ i }</span>`).appendTo($wheel);
  }

})(jQuery);
*,
*:before,
*:after {
  box-sizing: border-box;
}

.wheel {
  perspective: 1000px;
  border: 1px solid #333;
  margin: 50px;
}

.wheel .wheel__inner {
  background-color: red;
  position: relative;
  width: 200px;
  height: 350px;
  margin: 0 auto;
  transform-style: preserve-3d;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-name: spin;
  animation-duration: 6s;
}

.wheel .wheel__inner .wheel__segment {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 40px;
  position: absolute;
  top: 50%;
  background-color: #ccc;
}

.wheel .wheel__inner .wheel__segment:nth-child(even) {
  background-color: #ddd;
}

@-webkit-keyframes spin {
  0% {
    transform: rotateX(360deg);
  }
  50% {
    transform: rotateX(180deg);
  }
  100% {
    transform: rotateX(0deg);
  }
}
<script src=".3.1/jquery.min.js"></script>
<div class="wheel">
  <div class="wheel__inner">
  </div>
</div>

I have a 3D css wheel/cylindar animation that's rotating on the x axis. My issue is the animation appears to move up and down outside of it's container. Example GIF below...

The code for the above can be found here here: https://jsfiddle/thelevicole/bkt0v1mc/

(function($) {

  const $wheel = $('.wheel .wheel__inner');
  const items = 28;
  const diameter = $wheel.height();
  const radius = diameter / 2;
  const angle = 360 / items;
  const circumference = Math.PI * diameter;
  const height = circumference / items;

  for (let i = 0; i < items; i++) {
    var transform = `rotateX(${ angle * i }deg) translateZ(${ radius }px)`;

    $('<div>', {
      class: 'wheel__segment'
    }).css({
      'transform': transform,
      'height': height,
    }).html(`<span>Item ${ i }</span>`).appendTo($wheel);
  }

})(jQuery);
*,
*:before,
*:after {
  box-sizing: border-box;
}

.wheel {
  perspective: 1000px;
  border: 1px solid #333;
  margin: 50px;
}

.wheel .wheel__inner {
  background-color: red;
  position: relative;
  width: 200px;
  height: 350px;
  margin: 0 auto;
  transform-style: preserve-3d;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-name: spin;
  animation-duration: 6s;
}

.wheel .wheel__inner .wheel__segment {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 40px;
  position: absolute;
  top: 50%;
  background-color: #ccc;
}

.wheel .wheel__inner .wheel__segment:nth-child(even) {
  background-color: #ddd;
}

@-webkit-keyframes spin {
  0% {
    transform: rotateX(360deg);
  }
  50% {
    transform: rotateX(180deg);
  }
  100% {
    transform: rotateX(0deg);
  }
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wheel">
  <div class="wheel__inner">
  </div>
</div>

The red pane is the segments container, this is the element with the css animation. The red pane is staying within the container, however, the segments are offset vertically which is pushing them outside of the container.

I can stop this up and down movement by adding transform-origin: 50% 0; to each of the segments but this poses a new issue. Gaps between the segments! See below...

Code for the above: https://jsfiddle/thelevicole/bkt0v1mc/1/

( function( $ ) {

    const $wheel = $( '.wheel .wheel__inner' );
    const items = 28;
    const diameter = $wheel.height();
    const radius = diameter / 2;
    const angle = 360 / items;
    const circumference = Math.PI * diameter;
    const height = circumference / items;
    
    for ( let i = 0; i < items; i++ ) {
        var transform = `rotateX(${ angle * i }deg) translateZ(${ radius }px)`;

        $( '<div>', {
            class: 'wheel__segment'
        } ).css( {
            'transform': transform,
            'height': height,
        } ).html( `<span>Item ${ i }</span>` ).appendTo( $wheel );
    }
    
} )( jQuery );
*, *:before, *:after {
  box-sizing: border-box;
}

.wheel {
  perspective: 1000px;
  border: 1px solid #333;
  margin: 50px;
}
.wheel .wheel__inner {
  background-color: red;
  position: relative;
  width: 200px;
  height: 350px;
  margin: 0 auto;
  transform-style: preserve-3d;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-name: spin;
  animation-duration: 6s;
}
.wheel .wheel__inner .wheel__segment {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 40px;
  position: absolute;
  top: 50%;
  background-color: #ccc;
  transform-origin: 50% 0;
}
.wheel .wheel__inner .wheel__segment:nth-child(even) {
  background-color: #ddd;
}

@-webkit-keyframes spin {
  0% {
    transform: rotateX(360deg);
  }
  50% {
    transform: rotateX(180deg);
  }
  100% {
    transform: rotateX(0deg);
  }
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wheel">
    <div class="wheel__inner">
    </div>
</div>

Any help would be great!

Share Improve this question edited Sep 8, 2020 at 9:45 Temani Afif 275k28 gold badges365 silver badges486 bronze badges asked Sep 7, 2020 at 22:58 Levi ColeLevi Cole 3,6841 gold badge26 silver badges38 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

You can rectify this by adjusting the transform-origin of the rotated element like below:

transform-origin: 50% calc(50% + height/2);

( function( $ ) {

    const $wheel = $( '.wheel .wheel__inner' );
    const items = 28;
    const diameter = $wheel.height();
    const radius = diameter / 2;
    const angle = 360 / items;
    const circumference = Math.PI * diameter;
    const height = circumference / items;
    
    for ( let i = 0; i < items; i++ ) {
        var transform = `rotateX(${ angle * i }deg) translateZ(${ radius }px)`;

        $( '<div>', {
            class: 'wheel__segment'
        } ).css( {
            'transform': transform,
            'height': height,
        } ).html( `<span>Item ${ i }</span>` ).appendTo( $wheel );
    }
  $wheel.css('transform-origin','50% calc(50% + '+height/2+'px)');
  $wheel.css('margin-top','-'+height+'px'); /* negative margin here to keep the element into the center */
    
} )( jQuery );
*, *:before, *:after {
  box-sizing: border-box;
}

.wheel {
  perspective: 1000px;
  border: 1px solid #333;
  margin: 50px;
}
.wheel .wheel__inner {
  background-color: red;
  position: relative;
  width: 200px;
  height: 350px;
  margin: 0 auto ; 
  transform-style: preserve-3d;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-name: spin;
  animation-duration: 6s;
}
.wheel .wheel__inner .wheel__segment {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 40px;
  position: absolute;
  top: 50%;
  background-color: #ccc;
}
.wheel .wheel__inner .wheel__segment:nth-child(even) {
  background-color: #ddd;
}

@-webkit-keyframes spin {
  0% {
    transform: rotateX(360deg);
  }
  50% {
    transform: rotateX(180deg);
  }
  100% {
    transform: rotateX(0deg);
  }
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wheel">
    <div class="wheel__inner">
    </div>
</div>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Wheel Menu with JSON Events</title>
    <script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            display: flex;
            justify-content: flex-end;
            align-items: center;
            min-height: 100vh;
            background: #f5f5f5;
            font-family: Arial, sans-serif;
        }
        .wheel-container {
            perspective: 1000px;
            width: 250px;
            height: 400px;
            position: relative;
            border: 0px solid #000;
            padding: 35px;
            display: flex;
            justify-content: center;
            align-items: center;
            position: fixed;
            right: 0;
        }
        .wheel {
            width: 200px;
            height: 350px;
            position: relative;
            margin: 0 auto;
            transform-style: preserve-3d;
            transition: transform 0.1s linear;
            transform: rotateX(0deg);
        }
        .wheel__segment {
            position: absolute;
            width: 100%;
            height: 40px;
            top: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #ddd;
            border: 1px solid #aaa;
            transform-origin: 50% 0;
            color: #333;
            font-size: 14px;
            font-weight: bold;
            transition: box-shadow 0.3s ease;
        }
        .wheel__segment span {
            transform: translateZ(120px);
        }
        .wheel__segment:hover {
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
        }
        /* Styling for contentview */
        #contentview {
            position: absolute;
            left: 30px;
            top: 50px;
            width: 400px;
            padding: 20px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div class="wheel-container">
        <div class="wheel"></div>
    </div>

    <!-- Contentview div where item info will be shown -->
    <div id="contentview">
        <h2>Practical application of the source code and ideas of this article.</h2><br>
        <p id="item-info">Click on an item to see its details here.</p>
    </div>

    <script>
    (function($) {
        const spinSound = new Audio('https://cdn.pixabay./download/audio/2025/01/19/audio_fca0fdbc60.mp3?filename=wind-swoosh-short-289744.mp3');
        const $wheel = $('.wheel');
        const segmentCount = 20;
        const segmentAngle = 360 / segmentCount;
        const wheelHeight = $wheel.height();
        const radius = wheelHeight / 2;
        const segmentHeight = (2 * Math.PI * radius) / segmentCount;

        // Data for items on the wheel
        const items = [
            { 
                id: 1, 
                title: 'Item 1', 
                Action: 'click', 
                Event: () => displayContent('Item 1', 'Details of Item 1') 
            },
            { 
                id: 2, 
                title: 'Item 2', 
                Action: 'dblclick', 
                Event: () => displayContent('Item 2', 'Details of Item 2') 
            },
            { 
                id: 3, 
                title: 'Item 3', 
                Action: 'click', 
                Event: () => displayContent('Item 3', 'Details of Item 3') 
            },
            { 
                id: 4, 
                title: 'Item 4', 
                Action: 'dblclick', 
                Event: () => displayContent('Item 4', 'Details of Item 4') 
            },
            { 
                id: 5, 
                title: 'Item 5', 
                Action: 'click', 
                Event: () => displayContent('Item 5', 'Details of Item 5') 
            },
            { 
                id: 6, 
                title: 'Item 6', 
                Action: 'dblclick', 
                Event: () => displayContent('Item 6', 'Details of Item 6') 
            }
        ];

        // Extend items array to match segment count
        const extendedItems = [];
        for (let i = 0; i < segmentCount; i++) {
            extendedItems.push(items[i % items.length]);
        }

        // Function to create segments on the wheel
        for (let i = 0; i < segmentCount; i++) {
            const angle = segmentAngle * i;
            const item = extendedItems[i];
            const $segment = $('<div>', {
                class: 'wheel__segment',
                'data-index': i
            }).css({
                'transform': `rotateX(${angle}deg) translateZ(${radius}px)`,
                'height': segmentHeight
            }).html(`<span>${item.title}</span>`).appendTo($wheel);

            // Attach event handlers
            $segment.on(item.Action, function() {
                item.Event();  // Trigger event from JSON data
            });
        }

        // Function to update contentview div with item details
        function displayContent(title, details) {
            $('#item-info').html(`<strong>${title}</strong><br>${details}`);
        }

        // Function to handle the size of the wheel dynamically
        function changeWheelSize(width, height) {
            const $container = $('.wheel-container');
            const $wheel = $('.wheel');
            $container.css({
                width: width + 'px',
                height: height + 'px'
            });

            $wheel.css({
                width: (width - 70) + 'px',
                height: (height - 70) + 'px'
            });

            const newWheelHeight = $wheel.height();
            const newRadius = newWheelHeight / 2;
            const newSegmentHeight = (2 * Math.PI * newRadius) / segmentCount;

            $('.wheel__segment').each(function(i) {
                const angle = segmentAngle * i;
                $(this).css({
                    'transform': `rotateX(${angle}deg) translateZ(${newRadius}px)`,
                    'height': newSegmentHeight
                });
            });
        }

        // Call function to adjust wheel size
        changeWheelSize(250, 500);

        let currentRotation = 0;
        let isDragging = false;
        let startY = 0;
        let lastY = 0;
        let lastTime = 0;
        let velocity = 0;
        let animationId = null;

        // Function to play sound when wheel rotates
        function playSpinSound() {
            spinSound.currentTime = 0;
            spinSound.play();
        }

        // Function to update wheel rotation
        function updateWheel() {
            $wheel.css('transform', `rotateX(${currentRotation}deg)`);
            playSpinSound();
        }

        // Mouse and touch event handlers for dragging
        $wheel.on('mousedown touchstart', function(e) {
            e.preventDefault();
            isDragging = true;
            startY = getEventY(e);
            lastY = startY;
            lastTime = performance.now();
            cancelAnimationFrame(animationId);
            velocity = 0;
        });

        $(document).on('mousemove touchmove', function(e) {
            if (!isDragging) return;
            e.preventDefault();
            const currentY = getEventY(e);
            const deltaY = currentY - lastY;
            currentRotation -= deltaY * 0.5;
            velocity = -deltaY / (performance.now() - lastTime) * 15;
            lastY = currentY;
            lastTime = performance.now();
            updateWheel();
        });

        $(document).on('mouseup touchend', function() {
            if (!isDragging) return;
            isDragging = false;
            if (Math.abs(velocity) > 0.5) {
                applyMomentum();
            }
        });

        function getEventY(e) {
            return e.type.includes('touch') ? e.touches[0].pageY : e.pageY;
        }

        function applyMomentum() {
            const friction = 0.96;
            velocity *= friction;
            if (Math.abs(velocity) > 0.5) {
                currentRotation += velocity;
                updateWheel();
                animationId = requestAnimationFrame(applyMomentum);
            }
        }
    })(jQuery);
    </script>
</body>
</html>

发布评论

评论列表(0)

  1. 暂无评论