CSS/자바스크립트:여러 상태로 회전 원형 메뉴를 만드는 방법은?
보통 저는 다른 사람의 스레드를 통해 제가 필요한 것을 찾는다고 스스로 게시하지 않기 때문에 이 중 하나가 잘못된 위치에 있거나 형식이 부적절하다면 죄송합니다.이거 진짜 처음 해봐요.
상황은 이렇습니다.
웹사이트를 재구축하려고 하는데 워드프레스용 X 테마를 선택했습니다.대부분 잘 되고 있지만, 몇 번은 X를 커스터마이징해서 돌아다니고 싶었지만, 조금 더 어려운 것으로 드러났습니다.맞춤 코딩을 하지 않고도 X 안에서 이를 수행할 수 있는 방법을 알고 계시다면 귀를 쫑긋 세웁니다.
그래서 제가 하려는 것은 다음과 같습니다.
저는 메뉴의 "선택된" 요소를 맨 위에 위치시키는 원형 메뉴에 대한 아이디어를 생각해 냈습니다.따라서 레이아웃 측면에서는 다음과 같이 보일 수 있습니다.
(미안해요, 제 게시물에 이미지를 사용하기엔 너무 처음이라네요 :/)
기본 상태: https://i.stack.imgur.com/Gs2Nz.jpg
이제 사용자가 항목을 클릭할 때, 이전 이미지에서 "1" 항목이 있던 상단으로 새로운 선택 항목을 돌려야 합니다.그래서 이렇게 될 것입니다.
사용자가 항목 3을 선택한 경우 메뉴 항목이 순환됨: https://i.stack.imgur.com/KWseu.jpg
그 밖에 유의할 사항메뉴 항목의 텍스트 또는 이미지가 항상 정상적으로 정렬되도록 합니다. 즉, 요소 텍스트가 회전한 후에 뒤집히거나 하는 것을 원하지 않습니다.
CSS 스타일로 하드코딩된 것이 아니라 페이지가 로드될 때 다루고 싶은 요소의 원래 위치입니다.주로 역동적으로 할 수 있도록 하기 위해서입니다.
메뉴를 좀 더 해보려고 하는데 제가 문제가 있는 건 이런 행동이 행동입니다.
Jquery의 Animate() 메서드나 자바스크립트를 사용하여 각 요소 css "top" 및 "left" 속성에 영향을 주는 등의 작업을 시도해 보았지만 요소가 움직이지 않는 것 같습니다.
X의 커스터마이저 영역을 통과하려고 해도 문제가 되지 않는다는 것을 모르겠습니다. 바로 거기서 자바스크립트 코드를 추가하라고 했기 때문입니다.아니면 이것은 제가 자바스크립트/JQuery 코드를 CSS와 제대로 연결하지 않는 것과 관련이 있을 수 있습니다. 저는 상당한 코딩 경험이 있지만 JQuery/CSS 등은 비교적 처음입니다.
So short version:페이지가 로드될 때 요소가 중심점을 중심으로 동적으로 배치되는 방법을 찾고 있습니다.그런 다음 사용자가 요소를 클릭하면 새로 선택한 항목이 상단에 있을 때까지 모든 요소가 중앙을 중심으로 회전합니다.이 동작은 사용자가 다른 항목을 선택할 때 계속 수행해야 합니다.
긴 글이라 죄송합니다만, 최대한 설명하려고 노력하고 있습니다.어떤 통찰력이나 조언이라도 주시면 대단히 감사하겠습니다!미리 감사드립니다! :)
업데이트: 그래서 제가 원하는 것에 딱 맞는 것 같아 마젤린의 대답을 시도해 보았습니다.하지만 엑스테마의 자바스크립트 영역에 추가하고 CSS를 업데이트하니 요소가 움직이지 않습니다.다 중앙에 쌓이는데 중앙을 안 감싸고 클릭해도 아무 효과가 없는 것 같습니다.CSS가 영향을 준 것 같은데 자바스크립트 부분이 어떤 이유로 요소에 영향을 미치지 않는 것 같습니까?
제가 사용한 marzelin의 답변은 다음과 같습니다(JavaScript 부분만 해당).
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'
})
}
}
다음은 내 X-테마의 자바스크립트 섹션의 현재 모습입니다(내 탐색바 숨기기 등 다른 기능에 대한 다른 코드는 제외).
jQuery(function($){
/* javascript or jquery code goes here */
const stars = Array.from(document.querySelectorAll('.btnStars'));
const count = stars.length;
const increase = Math.PI * 2 / stars.length;
const radius = 300;
let angle = 0;
stars.forEach((star, i) => {
star.style.top = Math.sin(-Math.PI / 2 + i * increase) * radius + 'px';
star.style.left = Math.cos(-Math.PI / 2 + i * increase) * radius + 'px';
});
$('.btnStar').click(function(e) {
const n = stars.indexOf(e.target);
const endAngle = (n % count) * increase;
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;
}
stars.forEach((star, i) => {
star.style.top = Math.sin(-Math.PI / 2 + i * increase - angle) * radius + 'px';
star.style.left = Math.cos(-Math.PI / 2 + i * increase - angle) * radius + 'px';
})
}
turn();
});
});
CSS 클래스 이름 등 몇 가지를 바꿨지만 대부분 똑같습니다.X 테마의 편집자가 몇 가지 기능이 무엇인지 모르는 것 같아 전화가 오기 전에 이동시켰더니 다시 찾은 것 같아 재정비하는 등 몇 가지 작업을 했습니다.그런 작은 것들.
이동 기능을 JQuery .click 기능으로 변경하여 트리거가 되는지 확인해 보았지만 아무것도 변경되지 않은 것 같습니다.
전에 자바스크립트와 JQuery를 작업한 적은 있지만 워드프레스 테마에 통합하려는 시도를 실제로 다룬 적은 없어서 이게 무엇이 작동하지 않는지 정말 모르겠습니다.
제가 잘못하고 있는 것을 본 사람이 있습니까?이게 왜 안 되는지 너무 당황스러워서요 :/
단순 MVP
const buttons = Array.from(document.querySelectorAll('.button'))
const count = buttons.length
const increase = Math.PI * 2 / buttons.length
const radius = 150
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)
buttons.forEach((button, i) => {
button.style.top = Math.sin(-Math.PI / 2 + (i - n % count) * increase) * radius + 'px'
button.style.left = Math.cos(-Math.PI / 2 + (i - n % count) * increase) * 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;
}
.center {
width: 100px;
height: 100px;
background-color: goldenrod;
border-radius: 100%;
position: relative;
line-height: 100px;
text-align: center;
}
.button {
position: absolute;
width: 100px;
height: 100px;
border-radius: 100%;
-webkit-transition: all 0.5s;
transition: all 0.5s;
background-color: pink;
line-height: 100px;
text-align: center;
}
<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>
원운동
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>
여기서 삼각법은 잘못된 느낌이 듭니다.
이것은 마치 이진 코드로 프로그래밍을 하는 것과 같습니다.나중에 코드를 읽을 수 있는 능력을 유지하고 논리를 더 수정하고 싶다면 프로그램이 어떤 모습이어야 하는지는 가능하지만 반드시 그럴 필요는 없습니다.
각 메뉴 요소의 위치를 계산할 필요가 없도록 각 축의 회전과 메뉴의 회전을 분리해야 합니다.
이들이 분리되면 CSS 변수에 값을 배치하여 각각의 버튼을 뒤로 회전시키면서 목표로 하는 요소(메뉴 또는 축)를 둘 다 회전시킬 수 있습니다.이렇게 하면 버튼이 항상 똑바로 서 있을 것입니다. 왜냐하면 회전이 서로 상쇄되기 때문입니다.
여기 원리에 대한 데모가 있습니다.CSS 변수의 사용에 주목하고,
사용.style="{ '--var-name': value }"
. 런타임 중에 마크업을 검사하여 현재 회전 값을 읽을 수도 있습니다.
new Vue({
el: '#app',
data: () => ({
buttons: 3,
useTransitions: true,
isMenuOpen: true,
rotation: -90
}),
computed: {
axisRotations() {
return Array.from({
length: this.buttons
}).map((_, i) => 360 * (this.buttons - i) / this.buttons)
},
menuRotation: {
get() {
return this.rotation
},
set(val) {
this.rotation = isNaN(Number(val)) ? -90 : Number(val)
}
}
},
methods: {
updateButtons(n) {
if (this.buttons + n > 0) {
this.buttons += n;
this.isMenuOpen = true;
this.menuRotation = -90;
}
},
goToTop(axis) {
let diff = this.degreesToTop(axis);
diff = diff > 180
? diff - 360
: diff <= -180
? diff + 360
: diff;
this.menuRotation = Math.round((this.menuRotation + diff) * 10) / 10;
},
degreesToTop(axis) {
return (Math.round(this.axisRotations[axis - 1]) - this.menuRotation - 90) % 360;
},
isActive(axis) {
return !(this.degreesToTop(axis));
},
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
})
.menu {
width: 0;
height: 0;
top: 110px;
left: 110px;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
transform: rotate(var(--menu-rotation));
--menu-rotation: 0deg;
}
.menu .center {
height: 54px;
min-width: 54px;
border-radius: 27px;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border: 1px solid #eee;
cursor: pointer;
z-index: 2;
transform: rotate(calc(-1 * var(--menu-rotation))) translateZ(0);
}
.menu .axis {
position: absolute;
width: 100px;
left: 0;
height: 0;
display: flex;
align-items: center;
justify-content: flex-end;
transform-origin: 0 0;
transform: rotate(var(--axis-rotation));
}
.animated .axis.axis {
transition: all .54s cubic-bezier(.4, 0, .2, 1);
}
.menu .axis.closed {
width: 27px;
transform: rotate(calc(var(--axis-rotation) + 180deg));
opacity: .1;
}
.axis.closed button,
.axis.active button {
color: white;
background-color: #f50;
}
.axis.active:not(.closed) {
z-index: 1;
}
.axis button {
background-color: white;
cursor: pointer;
width: 54px;
height: 54px;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
border-radius: 27px;
border: 1px solid #eee;
transform: rotate(calc(calc(-1 * var(--axis-rotation)) - var(--menu-rotation))) translateZ(0);
outline: none;
}
.flexer {
display: flex;
height: 240px;
padding-left: 220px;
}
.controls {
flex-grow: 1
}
input {
width: 100%;
}
label input {
width: auto;
}
label {
display: block;
margin-top: 1rem;
cursor: pointer;
}
.animated,
.animated .center,
.animated .axis,
.animated .axis>* {
transition: transform .35s cubic-bezier(.4, 0, .2, 1);
}
body {
background-color: #f8f8f8;
}
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<div id="app">
<div>
<div class="flexer">
<div class="menu"
:class="{ animated: useTransitions }"
:style="{'--menu-rotation': `${menuRotation}deg`}">
<div class="center" @click="toggleMenu">menu</div>
<div v-for="axis in buttons"
class="axis"
:class="{ closed: !isMenuOpen, active: isActive(axis) }"
:style="{'--axis-rotation': `${360 * (axis - 1) / buttons}deg`}">
<button v-text="axis" @click="goToTop(axis)" />
</div>
</div>
<div class="controls">
Menu rotation (<code v-text="`${menuRotation}deg`"></code>)
<input type="range" min="-720" max="720" v-model="menuRotation">
<button @click="updateButtons(1)">Add button</button>
<button @click="updateButtons(-1)">Remove button</button>
<button @click="toggleMenu">Toggle menu</button>
<label>
<input type="checkbox" v-model="useTransitions">Use transitions
</label>
</div>
</div>
<pre v-text="{ menuRotation, buttons, axisRotations }"></pre>
</div>
</div>
보시다시피, 저는 버튼의 위치를 절대 계산하지 않습니다.사용되는 유일한 삼각법은 "원 안에 360도가 있다"입니다.
위의 예는 Vue에서 행해집니다. 왜냐하면 제가 좋아하는 빠른 프로토타이핑 도구이기 때문입니다.바닐라 솔루션을 사용하여 제품을 최상의 상태로 만들고 싶다면, 이에 대한 후속 질문에 대한 제 답변을 참조하십시오.
언급URL : https://stackoverflow.com/questions/39428557/css-javascript-how-to-make-rotating-circular-menu-with-multiple-states
'programing' 카테고리의 다른 글
명령줄에서 XML을 예쁘게 인쇄하는 방법은 무엇입니까? (0) | 2023.09.14 |
---|---|
C# Double - ToString() 형식으로 소수점 두 자리를 사용하지만 반올림하지 않음 (0) | 2023.09.14 |
키 누르기 이벤트 후 jQuery.val() AFTER 이벤트를 받으려면 어떻게 해야 합니까? (0) | 2023.09.14 |
Android의 상태 표시줄 높이 (0) | 2023.09.14 |
WooCommerce - get_order()가 작동하지 않고 0을 반환합니다. (0) | 2023.09.14 |