簡體   English   中英

如何使用 javascript 確保元素僅在圓周上沿一個方向移動?

[英]How to make sure elements move only in one direction on circle using javascript?

好吧,我承認我在三角學方面真的很糟糕。 只是為了上下文,我將從我在這里提到的問題中添加一些內容。

參考問題: https://stackoverflow.com/a/39429290/168492

我正在嘗試構建圓形旋轉導航,但我已經陷入困境。

我想要達到的目標:

http://i.stack.imgur.com/Gs2Nz.jpg .

當您單擊任何一個元素時,我希望元素旋轉。 例如,如果用戶選擇了第 3 項,則會旋轉菜單項:

http://i.stack.imgur.com/KWseu.jpg .

到目前為止我已經能夠做到的。 我已經能夠使用sincos function 在圓形 plot 上渲染項目。 當我單擊任何元素時,我也能夠實現圓周運動(使用參考問題中的代碼 - https://stackoverflow.com/a/39429290/168492 )。 當我單擊最后一個元素時會出現問題。 它標志着整個導航向另一個方向移動,並在相反的方向上完成一個完整的旋轉。

下面是我能夠在圓上使用 position 元素的代碼:

const count = this.slides.length;
const increase = (Math.PI * 2) / this.slides.length;
const radius = 100;
let angle = 0;

var that = this;
this.slides = this.slides.map(function (item, i) {
  item.id = i;
  item.top = Math.sin(-Math.PI / 4 + i * increase) * radius + "px";
  item.left = Math.cos(-Math.PI / 4 + i * increase) * radius + "px";
  /* I tried adding an angle component (couldn't get it to work) */
  item.angle = Math.atan2(
    Math.sin(-Math.PI / 4 + i * increase) * radius,
    Math.cos(-Math.PI / 4 + i * increase) * radius
  );
  console.log((item.angle * 180) / Math.PI);
  return item;
});

我想要的是讓導航只向一個方向旋轉。 我該怎么做?

下面是觸發點擊旋轉的代碼:

function move(e) {
  const n = buttons.indexOf(e.target);
  var item = that.slides.find((slide) => slide.id == n);
  const endAngle = (n % count) * increase;

  turn();
  function turn() {
    if (Math.abs(endAngle - angle) > 1 / 12) {
      const sign = endAngle > angle ? 1 : -1;
      angle = angle + sign / 12;
      setTimeout(turn, 20);
    } else {
      angle = endAngle;
    }
    buttons.forEach((button, i) => {
      var item = that.slides.find((slide) => slide.id == n);

      button.style.top =
        Math.sin(-Math.PI / 4 + i * increase - angle) * radius + "px";
      button.style.left =
        Math.cos(-Math.PI / 4 + i * increase - angle) * radius + "px";
    });
  }
}

我嘗試將符號值更改為僅 1。但這會使菜單旋轉成無限循環。

完整片段:

 const buttons = Array.from(document.querySelectorAll('.button')) const count = buttons.length const increase = Math.PI * 2 / buttons.length const radius = 150 let angle = 0 buttons.forEach((button, i) => { button.style.top = Math.sin(-Math.PI / 2 + i * increase) * radius + 'px' button.style.left = Math.cos(-Math.PI / 2 + i * increase) * radius + 'px' button.addEventListener('click', move) }) function move(e) { const n = buttons.indexOf(e.target) const endAngle = (n % count) * increase turn() function turn() { if (Math.abs(endAngle - angle) > 1 / 8) { const sign = endAngle > angle? 1: -1 angle = angle + sign / 8 setTimeout(turn, 20) } else { angle = endAngle } buttons.forEach((button, i) => { button.style.top = Math.sin(-Math.PI / 2 + i * increase - angle) * radius + 'px' button.style.left = Math.cos(-Math.PI / 2 + i * increase - angle) * radius + 'px' }) } }
 html, body { height: 100%; }.menu { height: 100%; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; background-color: seagreen; -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; line-height: 100px; text-align: center; }.center { width: 100px; height: 100px; background-color: goldenrod; border-radius: 100%; position: relative; }.button { position: absolute; width: 100px; height: 100px; border-radius: 100%; background-color: pink; line-height: 100px; text-align: center; cursor: pointer; }
 <div class="menu"> <div class="center">menu <div class="button">1</div> <div class="button">2</div> <div class="button">3</div> <div class="button">4</div> <div class="button">5</div> </div> </div>

如果有人可以提供幫助,那就太好了!

你不需要三角函數來解決這個問題。 這只是算術。 除了你已經介紹過的“一個圓圈有 360 度”部分。

你有一個旋轉的菜單。 還有一些按鈕在旋轉時應該保持直立。 將菜單的旋轉放置在 CSS 變量中,並使用它來旋轉整個菜單並將按鈕向后旋轉相同的量。

為了定位按鈕,使用一些輔助包裝器(實際上是從中心開始的一些行),圍繞中心點旋轉,將按鈕放置在行的另一端,有效地使按鈕與菜單中心保持距離。 這些助手(軸)的旋轉只需要在開始時設置。 一旦設置,它們不再需要調整。 它們的旋轉量也需要放入一個變量中,因此按鈕使用它來反向旋轉相同的量。

現在,無論菜單或任何單個軸的旋轉如何,通過相同變量中的值反向旋轉的按鈕將始終直立。 通過更改菜單旋轉變量,您可以旋轉整個事物。 此外,通過更改每個軸的旋轉值,您可以根據需要創建精美的打開或關閉效果。 按鈕將始終保持直立。

顯然,如果你想為整個事物設置動畫,運動部分應該共享相同的transition屬性。 如果他們不這樣做,按鈕將不會一直直立。

看到它工作:

 const axes = [...document.querySelectorAll('.axis')]; axes.forEach((axis, i) => { const angle = 360 * i / axes.length; axis.style.setProperty('--axis-rotation', `${angle}deg`); }) let rotation = 0; // change it to adjust the initial position of buttons updateMenuRotation(rotation); function updateMenuRotation(deg) { document.querySelector('.menu').style.setProperty('--menu-rotation', `${deg}deg`); } function rotateMenu(steps) { rotation += 360 * steps / axes.length; updateMenuRotation(rotation); console.log('rotation:', rotation); }
 .menu { width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; --menu-rotation: 0deg; transform: rotate(var(--menu-rotation)); }.menu.center { transform: rotate(calc(-1 * var(--menu-rotation))); }.menu.axis { position: absolute; width: 100px; left: 100px; height: 0; display: flex; align-items: center; justify-content: flex-end; transform-origin: 0 0; transform: rotate(var(--axis-rotation)); }.axis > * { width: 70px; height: 70px; display: flex; align-items: center; justify-content: center; border-radius: 35px; border: 1px solid #ccc; transform: rotate(calc(calc(-1 * var(--axis-rotation)) - var(--menu-rotation))); }.menu, .menu.center, .menu.axis, .menu.axis > * { transition: transform.35s cubic-bezier(.4,0,.2,1); }.controls { z-index: 1; position: relative; }.controls button { cursor: pointer; }
 <div class="menu"> <div class="center">menu</div> <div class="axis"> <div>1</div> </div> <div class="axis"> <div>2</div> </div> <div class="axis"> <div>3</div> </div> <div class="axis"> <div>4</div> </div> <div class="axis"> <div>5</div> </div> </div> <div class="controls"> <button onclick="rotateMenu(1)">rotate +1</button> <button onclick="rotateMenu(-1)">rotate -1</button> <button onclick="rotateMenu(2)">rotate +2</button> <button onclick="rotateMenu(5)">rotate +5</button> </div>


現在,據我了解您的嘗試,我相信您希望在單擊按鈕時找到將按鈕放在頂部的最小菜單旋轉。 我故意沒有解決上面的這個問題,因為我認為它應該與將菜單旋轉特定數量的步驟的問題分開解決。

建議你自己嘗試解決。
如果你卡住了,下面是我解決它的方法。 我無法擺脫我過度復雜化的感覺,但我也看不到進一步簡化它的方法:

 const axes = [...document.querySelectorAll('.axis')]; axes.forEach((axis, i) => { const angle = 360 * i / axes.length; axis.style.setProperty('--axis-rotation', `${angle}deg`); axis.querySelector('div').addEventListener('click', rotateToTop); }) let rotation = -90; // change it to adjust the initial position of buttons updateMenuRotation(rotation); function updateMenuRotation(deg) { document.querySelector('.menu').style.setProperty('--menu-rotation', `${deg}deg`); } function rotateToTop(e) { const button = e.target; if (button) { [...document.querySelectorAll('.axis > div.active')].forEach(el => el.classList.remove('active')); button.classList.add('active'); rotateMenu( minStepsToTop( getRotation('axis', button), getRotation('menu', button), axes.length ) ); } } function minStepsToTop(aR, mR, aL) { // aR => axisRotatin // mR => menuRotation // aL => axis.length // angle => 360 / aL // stepsFromMenu => (((mR + 360) % 360) + 90) / angle; // stepsFromAxis => Math.round(aR / angle); // totalSteps => Math.round((((mR + 360) % 360) + 90) + aR) / angle); const totalSteps = Math.round(((((mR + 360) % 360) + 90) + aR) * aL / 360); // console.log(totalSteps); // totalSteps as closest number to 0 (positive or negative) const maxAbsoluteSteps = Math.floor(aL / 2); // 5 => 2; 6 => 3; 7 => 3, etc... return -(((totalSteps + maxAbsoluteSteps + aL) % aL) - maxAbsoluteSteps); } function getRotation(type, target) { return +(getComputedStyle(target).getPropertyValue(`--${type}-rotation`).replace('deg', '')); } function rotateMenu(steps) { rotation += 360 * steps / axes.length; updateMenuRotation(rotation); }
 .menu { width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; --menu-rotation: 0deg; transform: rotate(var(--menu-rotation)); }.menu.center { transform: rotate(calc(-1 * var(--menu-rotation))); }.menu.axis { position: absolute; width: 100px; left: 100px; height: 0; display: flex; align-items: center; justify-content: flex-end; transform-origin: 0 0; transform: rotate(var(--axis-rotation)); }.axis > * { width: 54px; height: 54px; display: flex; align-items: center; justify-content: center; border-radius: 27px; border: 1px solid #ccc; transform: rotate(calc(calc(-1 * var(--axis-rotation)) - var(--menu-rotation))); cursor: pointer; }.axis.active { background-color: #212121; color: white; }.menu, .menu.center, .menu.axis, .menu.axis > * { transition: transform.35s cubic-bezier(.4,0,.2,1); }.controls { z-index: 1; position: relative; }.controls button { cursor: pointer; }
 <div class="menu"> <div class="center">menu</div> <div class="axis"> <div class="active">1</div> </div> <div class="axis"> <div>2</div> </div> <div class="axis"> <div>3</div> </div> <div class="axis"> <div>4</div> </div> <div class="axis"> <div>5</div> </div> <div class="axis"> <div>6</div> </div> <div class="axis"> <div>7</div> </div> </div>

應該可以使用任意數量的按鈕,但我還沒有嘗試過。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM