I have the code set up so that when the user inputs a negative number or an invalid value for the thickness of the pipe (greater than 1/3rd of the diameter), an error message pops up. However, when I enter a valid input, the error message does not go away, and only goes away when updating the value of the outer diameter for the circle. How do I make it so that the moment I enter a valid input for the thickness, the error message goes away, or the moment I delete the invalid input, the error message goes away.
Additionally, I am using this program on an actual webpage with a formidable forms calculator. The size of the circle is taken from the inputs for the calculator as I am trying to create a graphic visual for the user. However, after hitting the "calculate" button, the inputs in the form no longer dynamically change the size of the circle and I have to completely refresh the page to fix the issue. Why is this happening and how do I fix this?
$(function() {
$('.circle').hide();
$('#outer_diameter').on('change', function() {
var $outer_diameter = parseFloat($("#outer_diameter").val()).toFixed(3);
var $converted = ($outer_diameter * 3.75).toFixed(3);
if ($outer_diameter > 85) {
$("#error").text($outer_diameter + " is too big").show();
return false;
}
if ($outer_diameter < 0) {
$('#error').text('Please input positive integers').show();
return false;
}
console.log($outer_diameter, $converted);
$('.circle').css({
height: (2 * $converted),
width: (2 * $converted),
top: "calc(50% - " + ($converted) + "px)",
left: "calc(50% - " + ($converted) + "px)"
});
$('.circle').fadeIn(300);
$('.circles').css({
height: (2 * $converted) + 10,
width: (2 * $converted) + 10
})
$('#error').hide();
})
$('.circle2').hide();
$('#inner_diameter').on('change', function() {
var $outer_diameter = parseFloat($("#outer_diameter").val()).toFixed(3);
var $inner_diameter = parseFloat($("#inner_diameter").val()).toFixed(3);
var $converted_2 = (($outer_diameter * 3.75) - (2 * ($inner_diameter * 3.75))).toFixed(3);
if ($outer_diameter > 85) {
$("#error")
return false;
}
if ($inner_diameter < 0) {
$('#error').text('Please input positive integers').show();
return false;
}
if ($inner_diameter >= 0.33 * $outer_diameter) {
$('#error').text('Wall Thickness invalid').show();
return false;
}
console.log($inner_diameter, $converted_2);
$('.circle2').css({
height: (2 * $converted_2),
width: (2 * $converted_2),
top: "calc(50% - " + ($converted_2) + "px)",
left: "calc(50% - " + ($converted_2) + "px)"
});
$('.circle2').fadeIn(300);
})
$('#error').hide();
});
body {
margin: 0 auto;
font-family: Helvetica, sans-serif;
}
.wrapper {
height: 400px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.circles {
margin-top: 50px;
position: relative;
}
.circle {
display: inline-block;
border-radius: 50%;
border-style: solid;
border-width: 2px;
border-color: blue;
background-color: rgba(0, 0, 0, 0);
z-index: -1;
left: 50%;
top: 50%;
position: absolute;
}
.circle2 {
display: inline-block;
position: absolute;
border-radius: 50%;
border-style: solid;
border-width: 2px;
border-color: red;
background-color: rgba(0, 0, 0, 0);
z-index: -1;
left: 50%;
top: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="title">
<h1>
Cutter Calculator
</h1>
</div>
<div class="Calculations">
<input type="number" id="outer_diameter" placeholder="Enter Outer Diameter"> <br>
<input type="number" id="inner_diameter" placeholder="Enter Thickness"> <br>
<input type="button" id="bttn" name="calculate" value="Calculate">
</div>
<div class="circles">
<span class="circle"></span>
<span class="circle2"></span>
</div>
<p id="error">
</p>
</div>
jsFiddle: https://jsfiddle.net/Kaevonz/6rc9jbo3/105/
The problem is that you have $('#error').hide(); outside the $('#inner_diameter').on('change', function() { so, if you change the function like this:
$('#inner_diameter').on('change', function() {
var $outer_diameter = parseFloat($("#outer_diameter").val()).toFixed(3);
var $inner_diameter = parseFloat($("#inner_diameter").val()).toFixed(3);
var $converted_2 = (($outer_diameter * 3.75) - (2 * ($inner_diameter * 3.75))).toFixed(3);
if ($outer_diameter > 85) {
$("#error")
return false;
}
if ($inner_diameter < 0) {
$('#error').text('Please input positive integers').show();
return false;
}
if ($inner_diameter >= 0.33 * $outer_diameter) {
$('#error').text('Wall Thickness invalid').show();
return false;
}
console.log($inner_diameter, $converted_2);
$('.circle2').css({
height: (2 * $converted_2),
width: (2 * $converted_2),
top: "calc(50% - " + ($converted_2) + "px)",
left: "calc(50% - " + ($converted_2) + "px)"
});
$('.circle2').fadeIn(300);
$('#error').hide();
})
works like you desire
For the second question, you don't have a onclick event binding the Calculate button...
Something like that:
$('#bttn').on('click', function() {
var $outer_diameter = parseFloat($("#outer_diameter").val()).toFixed(3);
var $inner_diameter = parseFloat($("#inner_diameter").val()).toFixed(3);
var $converted = ($outer_diameter * 3.75).toFixed(3);
var $converted_2 = (($outer_diameter * 3.75) - (2 * ($inner_diameter * 3.75))).toFixed(3);
if ($outer_diameter > 85) {
$("#error").text($outer_diameter + " is too big").show();
return false;
}
if ($outer_diameter < 0) {
$('#error').text('Please input positive integers').show();
return false;
}
console.log($outer_diameter, $converted);
$('.circle').css({
height: (2 * $converted),
width: (2 * $converted),
top: "calc(50% - " + ($converted) + "px)",
left: "calc(50% - " + ($converted) + "px)"
});
$('.circle').fadeIn(300);
$('.circles').css({
height: (2 * $converted) + 10,
width: (2 * $converted) + 10
})
console.log($inner_diameter, $converted_2);
$('.circle2').css({
height: (2 * $converted_2),
width: (2 * $converted_2),
top: "calc(50% - " + ($converted_2) + "px)",
left: "calc(50% - " + ($converted_2) + "px)"
});
$('.circle2').fadeIn(300);
$('#error').hide();
});
You can extract some methods for avoiding duplication
Related
i was recently diving into the world of css transform and wanted to rotate a div (x and y axis), i was able to do it with 2 sliders with 0 to 360 degree range but now looking forward to do it with dragging the mouse, i have made a very sloppy effort on that looking for suggestion to fix it:
jsfiddle link to test
"use strict";
let elContainer = $('#container');
let elBox = $('#box');
$(document).ready(function() {
$('.slider-rotate').on('input', function() {
sliderRotate();
});
elContainer.mousedown(function(e) {
initDragRotate(e);
});
elContainer.mousemove(function(e) {
dragRotate(e);
});
elContainer.mouseup(function(e) {
endDragRotate();
});
});
let dragging = false;
let delta = {};
function initDragRotate(e) {
dragging = true;
delta = {
x: e.pageX,
y: e.pageY,
};
}
function dragRotate(e) {
if (!dragging) {
return;
}
delta.x = e.pageX - delta.x;
delta.y = e.pageY - delta.y;
let rotateParam = '';
rotateParam += ' rotate' + 'Y' + '(' + delta.x + 'deg)';
rotateParam += ' rotate' + 'X' + '(' + delta.y + 'deg)';
elBox.css('transform', rotateParam);
}
function endDragRotate() {
if (!dragging) {
return;
}
dragging = false;
}
function sliderRotate() {
let rotateParam = '';
$('.slider-rotate').each(function() {
rotateParam += ' ' + getRotateParamString($(this));
});
elBox.css('transform', rotateParam);
}
function getRotateParamString(elClass) {
let val = elClass.val();
let rotateType = elClass.data('rotateType');
let rotateParam = 'rotate' + rotateType + '(' + val + 'deg)';
return rotateParam;
}
#container {
background: #ccc;
padding: 10vh;
display: flex;
align-items: center;
justify-content: center;
}
#box {
width: 30vh;
height: 30vh;
border: 5px solid #000;
position: relative;
transform-style: preserve-3d;
}
#box>div {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div id="box">
<div style="background: #f00;"></div>
<div style="background: #0f0;"></div>
</div>
</div>
<div id="control">
<br>
<label>
x
<input type="range" class="slider-rotate" data-rotate-type="X" min="0" max="360" value="0">
</label>
<br>
<label>
y
<input type="range" class="slider-rotate" data-rotate-type="Y" min="0" max="360" value="0">
</label>
</div>
also, there's 2 div on top and bottom (green and red) with transform-style preserve-3d property, hoping that it's show the other color when flipped but no luck! please suggest, thanks!
The calulation for how much to rotate is a little odd.
I believe what you need is to have the amount of rotation dependent on the amount the mouse is placed across and down the screen. To make this proportional to the screen size you need to divide it by the screen size and then multiply it by 360 to get the full range from pageX/Y being 0 to being at the right/bottom of the screen.
"use strict";
let elContainer = $('#container');
let elBox = $('#box');
$(document).ready(function() {
$('.slider-rotate').on('input', function() {
sliderRotate();
});
elContainer.mousedown(function(e) {
initDragRotate(e);
});
elContainer.mousemove(function(e) {
dragRotate(e);
});
elContainer.mouseup(function(e) {
endDragRotate();
});
});
let dragging = false;
let delta = {};
function initDragRotate(e) {
dragging = true;
delta = {
x: e.pageX,
y: e.pageY,
};
}
function dragRotate(e) {
if (!dragging) {
return;
}
// THIS IS THE CALCULATION THAT HAS CHANGED
delta.x = e.pageX / window.innerWidth * 360; //- delta.x;
delta.y = e.pageY / window.innerHeight * 360; // - delta.y;
let rotateParam = '';
rotateParam += ' rotate' + 'Y' + '(' + delta.x + 'deg)';
rotateParam += ' rotate' + 'X' + '(' + delta.y + 'deg)';
elBox.css('transform', rotateParam);
}
function endDragRotate() {
if (!dragging) {
return;
}
dragging = false;
}
function sliderRotate() {
let rotateParam = '';
$('.slider-rotate').each(function() {
rotateParam += ' ' + getRotateParamString($(this));
});
elBox.css('transform', rotateParam);
}
function getRotateParamString(elClass) {
let val = elClass.val();
let rotateType = elClass.data('rotateType');
let rotateParam = 'rotate' + rotateType + '(' + val + 'deg)';
return rotateParam;
}
#container {
background: #ccc;
padding: 10vh;
display: flex;
align-items: center;
justify-content: center;
}
#box {
width: 30vh;
height: 30vh;
border: 5px solid #000;
position: relative;
transform-style: preserve-3d;
}
#box>div {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div id="box">
<div style="background: #f00;"></div>
<div style="background: #0f0;"></div>
</div>
</div>
<div id="control">
<br>
<label>
x
<input type="range" class="slider-rotate" data-rotate-type="X" min="0" max="360" value="0">
</label>
<br>
<label>
y
<input type="range" class="slider-rotate" data-rotate-type="Y" min="0" max="360" value="0">
</label>
</div>
I am trying to zoom into those objects using a slider. Unfortunately, the bigger the distance from the nearest to deepest object, the faster the slide happens. How can control this behaviour so that the slide appears smooth?
What I tried: debouncing the handler and giving the circles a transition.
Here is a snippet:
const input = document.querySelector("input");
const circles = document.querySelectorAll(".circle");
let firstDepth = 9300;
let secondDepth = 100;
let thirdDepth = 2;
const initialSize = 0.0001;
circles[0].style.transform = "scale(" + (firstDepth * initialSize) + ")";
circles[1].style.transform = "scale(" + (secondDepth * initialSize) + ")";
circles[2].style.transform = "scale(" + (thirdDepth * initialSize) + ")";
input.addEventListener("input", function() {
//console.log(1)
circles[0].style.transform = "scale(" + firstDepth * input.value + ")";
circles[1].style.transform = "scale(" + secondDepth * input.value + ")";
circles[2].style.transform = "scale(" + thirdDepth * input.value + ")";
})
.circle {
width: 100px;
height: 100px;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
border-radius: 100%;
}
.first:nth-of-type(1) {
border: 2px solid red;
}
.second:nth-of-type(2) {
border: 2px solid orange;
}
.third:nth-of-type(3) {
border: 2px solid green;
}
<input type="range" value="0.0001" step="any" min="0.0001" max="1">
<div class="circle first"></div>
<div class="circle second"></div>
<div class="circle third"></div>
You would want an effect where an increase of the slide value with d would multiply the scale with some m, so that if you slide with 2d, you get a relative scaling with m2.
So, ... you need a transformation of input.value where that value appears in an exponent. In other words: the scale should increase exponentially in terms of the value of the slider.
Given that you want to see the first circle completely when the slider is at the left (scaling with initialSize) and the third circle completely when the slider is at the right, you can derive the following formula for that transformation:
initialSize * (firstDepth / thirdDepth) ** (input.value - initialSize);
Here is the code adapted to that:
const input = document.querySelector("input");
const circles = document.querySelectorAll(".circle");
let firstDepth = 9300;
let secondDepth = 100;
let thirdDepth = 2;
const initialSize = 0.0001;
circles[0].style.transform = "scale(" + (firstDepth * initialSize) + ")";
circles[1].style.transform = "scale(" + (secondDepth * initialSize) + ")";
circles[2].style.transform = "scale(" + (thirdDepth * initialSize) + ")";
input.addEventListener("input", function() {
let coeff = initialSize * (firstDepth / thirdDepth) ** (input.value - initialSize);
circles[0].style.transform = "scale(" + firstDepth * coeff + ")";
circles[1].style.transform = "scale(" + secondDepth * coeff + ")";
circles[2].style.transform = "scale(" + thirdDepth * coeff + ")";
})
.circle {
width: 100px;
height: 100px;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
border-radius: 100%;
}
.first:nth-of-type(1) {
border: 2px solid red;
}
.second:nth-of-type(2) {
border: 2px solid orange;
}
.third:nth-of-type(3) {
border: 2px solid green;
}
<input type="range" value="0.0001" step="any" min="0.0001" max="1">
<div class="circle first"></div>
<div class="circle second"></div>
<div class="circle third"></div>
I have created a very basic animated percentage bar with HTML, CSS, and JS the only problem is that I am trying to devise a way to also animate the increase and/or decrease of the percentage output to go along with an animated percentage bar. In the example below and in this JsFiddle I have successfully created that with the only problem being that it doesn't seem to be the most efficient or effective way of doing it.
In the code snippet directly below I'm creating this animated effect by...
Setting x equal to setInterval
Capturing the width of percent bar on the left and removing the px from the end of the string.
Capturing the width of percent bar on the right and removing the px from the end of the string.
Displays the percent value for the left (blue) bar inside the tooltip that can be seen when hovered over.
Displays the percent value for the right (red) bar inside the tooltip that can be seen when hovered over.
Displays the percent value of the left (blue) bar below the percent bar.
Displays the percent value of the right (red) bar below the percent bar.
All of this code below will run every 64 Milliseconds.
This code will only run for 2000 Milliseconds which is the same amount of time that I have set the transition for the percent bars.
Note: The whole point of the code below is to give the illusion that the percent values are increasing as either of the percent bars are increasing. In short, the goal is to make it seem more animated rather than the number all of a sudden seeing the number jump from one number to the next.
There just has to be a better way of achieving the same effect (or better) rather than pulling data from the DOM every 64 Milliseconds. There are tons of real-time graph's out on the web that achieve the same effect but I can't figure out how so I came up with my own and don't really think that they do it this way either. Any ideas??? I would only like to use pure Javascript with no libraries such as jQuery.
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
var good = document.querySelector('#good');
var bad = document.querySelector('#bad');
var p_bar_left = document.querySelector('#progressbar-left');
var p_bar_right = document.querySelector('#progressbar-right');
var counter_left = 0;
var counter_right = 0;
var percent_left = 0;
var percent_right = 0;
function changePercent(increment, which) {
if (which == 'left') {
counter_left += increment;
} else if (which == 'right') {
counter_right += increment;
} else {
throw "Don't know which value to increase.";
}
percent_left = (counter_left / (counter_left + counter_right)) * 100;
percent_right = (counter_right / (counter_left + counter_right)) * 100;
p_bar_left.style.width = percent_left + '%';
p_bar_right.style.width = percent_right + '%';
document.querySelector('#total-amount').innerText = counter_right + counter_left;
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
}
good.addEventListener('click', function() {
changePercent(1, 'left');
});
bad.addEventListener('click', function() {
changePercent(1, 'right');
});
var tooltip = document.querySelectorAll('.tooltip');
var tooltipelement = document.querySelectorAll('#progressbar-left, #progressbar-right');
for (var x = tooltipelement.length; x--;) {
tooltipelement[x].addEventListener('mousemove', function(e) {
for (var i = tooltip.length; i--;) {
tooltip[i].style.left = e.pageX + 20 + 'px';
tooltip[i].style.top = e.pageY + 'px';
}
});
}
#progressbar-container {
display: flex;
position: relative;
width: 50vw;
height: 32px;
border: 2px solid black;
background-color: #ccc;
justify-content: space-between;
}
#progressbar-left {
position: relative;
height: 100%;
background-color: blue;
transition: width 2s;
align-items: center;
justify-content: center;
}
#progressbar-right {
position: relative;
height: 100%;
background-color: red;
transition: width 2s;
align-items: center;
justify-content: center;
}
.tooltip {
display: none;
position: fixed;
width: auto;
height: auto;
padding: 6px;
background-color: black;
text-align: center;
border-radius: 6px;
z-index: 1;
}
.object {
display: inline-block;
color: #fff;
}
.percent-value {
display: inline-block;
color: #fff;
}
#progressbar-left:hover .tooltip {
display: block;
}
#progressbar-right:hover .tooltip {
display: block;
}
#total {
display: block;
font-weight: bold;
}
#total-amount {
display: inline-block;
font-weight: normal;
}
#blue,
#red {
display: block;
font-weight: bold;
}
.percent-amount {
display: inline-block;
font-weight: normal;
}
<body>
<input type="button" value="Good" id="good">
<input type="button" value="Bad" id="bad">
<div id="progressbar-container">
<div id="progressbar-left">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Blue</span>
<span class="percent-value"></span>
</span>
</div>
</div>
<div id="progressbar-right">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Red</span>
<span class="percent-value"></span>
</span>
</div>
</div>
</div>
<span id="total">Total: <p id="total-amount">0</p></span>
<span id="blue">Percent Blue: <p class="percent-amount">0%</p></span>
<span id="red">Percent Red: <p class="percent-amount">0%</p></span>
</body>
JsFiddle
Normally I don't post myself I usually find what I need via other's threads so I'm sorry if any of this is in the wrong place or improperly formatted. I've never done this before really.
So here's the situation:
I'm trying to rebuild my website and I opted to go with the X Theme for WordPress. Mostly it's going great, but the few times I've wanted to customize and go around X it's proved a bit more difficult. If you know of a way to do this within X that would accomplish this without doing custom coding, I'm all ears.
So here's what I'm trying to do:
I had an idea for a circular menu that would position it's elements to where the top one is the "selected" element of the menu. So it would look something like this in terms of layout:
(Sorry, apparently I'm too new to use images in my posts :/)
Basic State: http://i.stack.imgur.com/Gs2Nz.jpg
Now when a user were to click on an item, I'd like it to rotate the new selected item up to the top where the "1" item was in the previous image. So it would be like this:
Menu Items Are Rotated If The User Selected Item 3: http://i.stack.imgur.com/KWseu.jpg
Some other things to note:
I want the text or images of the menu items to always be normally aligned, in other words I don't want the elements text to be upside down or something after it rotates.
The original positioning of the elements I'd like to be handle when the page loads instead of hardcoded in the CSS style. Mainly just so that it can be done dynamically.
I'm planning to do more with the menu but it's THIS behavior that I'm having problems with.
I've tried things like Jquery's Animate() method, or using JavaScript to affect each elements css "top" & "left" properties, but it just doesn't seem to be working as the elements don't seem to want to move.
I don't know that this isn't a problem with trying to go through X's customizer area or not as that's where I was told to add JavaScript code. Or this could have to do with me not connecting the JavaScript/JQuery code with the CSS properly, I have a decent amount of coding experience, but I'm relatively new to JQuery/CSS etc.
So short version:
I'm trying to find a way that when the page loads the elements are positioned dynamically around a center point. Then when a user clicks on an element all the elements rotate around the center, till the newly selected item is at the top. This behavior should continue as the user selects different items.
Sorry for this being a long post, but I'm just trying to explain as best I can. Any insight or advice would be greatly appreciated! Thanks in advance! :)
UPDATE:
So I ended up trying marzelin's answer as it looked perfect for what I wanted. However, when I added it into the X-Theme's Javascript area and updated my CSS the elements aren't moving. They all stack in the center, but they don't encircle the center point and clicking on them doesn't seem to be doing anything. Seems like the CSS has taken affect but the Javascript part is not affecting the elements for some reason?
Here's the marzelin's answer I used (just the JavaScript part):
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'
})
}
}
Here's how my the javascript section of my X-Theme looks at the moment (excluding other code for other functions, like hiding my navbar and such):
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();
});
});
I did change a few things, namely the CSS class names and such, but most of it's the same. I did a couple things like reorganizing as the editor for X Theme didn't seem to know what a few of the functions were so I moved them to before their calls and then it seemed to find them. So little things like that.
I also tried to change the move function to a JQuery .click function to see if that would trigger anything but it didn't seem to change anything.
While I've worked with Javascript and some JQuery before, I've never really dealt with trying to incorporate it into a WordPress theme so I really don't know what this isn't working.
Does anyone see anything I'm doing wrong? Cause I'm pretty perplexed as to why this won't work. :/
Simple 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>
Circular Motion
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>
The trigonometrical approach here feels wrong.
It's pretty much like trying to program in binary code. It's doable, but not necessarily how our programs should look like, if we want to keep our ability to read the code later on and maybe further modify its logic.
In order not to have to calculate the position of each menu element, we'd have to separate the rotation of the menu from the rotation of each axis.
Once those are separated, their values could be placed in CSS variables, rotating both the element they're aimed at (menu or axis) while rotating the respective button backwards by the same amount. This way, the buttons will always stand upright, because the rotations cancel each other out.
Here's a demo of the principle. Notice the use of CSS variables,
using style="{ '--var-name': value }". You can also inspect the markup during runtime to read the current rotation values:
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>
As you can see, I'm never calculating positions of buttons. The only trigonometry used is "there are 360 degrees in a circle".
The above example is done in Vue, as it's a fast prototyping tool I happen to like. If you want a vanilla solution of getting the items to top, see my answer on a follow-up question to this one.
These are my javascript, css, and html files for simple web page for dragging an element and putting it into the container(div) :
var xContainer;
var yContainer
var insideFirst = false;
$(window).load(
function() {
xContainer = $("#center").offset().left;
yContainer = $("#center").offset().top;
console.log("ready");
$("#center").on("click", function(event) {
console.log(event);
});
$("#center").droppable();
$("#firstID").draggable({
cursor : 'move',
revert: "invalid"
});
$("#firstID").mousedown(function() {
isDragging = false;
}).mousemove(
function() {
isDragging = true;
var x = $("#firstID").offset().left;
var y = $("#firstID").offset().top;
xContainer = $("#center").offset().left;
yContainer = $("#center").offset().top;
var xPosition = (x - xContainer).toFixed(2);
var yPosition = (y - yContainer).toFixed(2);
console.log("center:" + xContainer);
console.log("x:" + x);
console.log("difference: "+(x-xContainer));
/*
* if (x < 750 && y < 750) {
* $("#firstID").css('background','#ffffff') }
*/
if (xPosition >= 0.0 && yPosition >= 0.0
&& xPosition <= 500.0 && yPosition <= 200.0) {
$("#xPosition").val("x:" + xPosition);
$("#yPosition").val("y:" + yPosition);
$("#firstID").css('background', '#e4e4e4')
$('#firstID').draggable({containment : [xContainer,yContainer,xContainer+500,yContainer+200.0] });
} else {
//$("#xPosition").val("GO IN!!!");
//$("#yPosition").val("GO IN!!!");
$("#firstID").css('background', '#d1f1fc')
}
}).mouseup(function() {
var wasDragging = isDragging;
isDragging = false;
if (!wasDragging) {
console.log("clicked");
}
});
$("#secondID").draggable({
cursor : 'move'
});
$("#secondID").mousedown(function() {
isDragging = false;
}).mousemove(function() {
isDragging = true;
var x = $("#secondID").offset().left;
var y = $("#secondID").offset().top;
// $("#text-x").val ("x:"+x);
// $("#text-y").val ("y:"+y);
}).mouseup(function() {
var wasDragging = isDragging;
isDragging = false;
if (!wasDragging) {
console.log("clicked");
}
});
$("#secondID").dblclick(function() {
rotation += 90;
$(this).rotate(rotation);
});
});
jQuery.fn.rotate = function(degrees) {
$(this).css({
'-webkit-transform' : 'rotate(' + degrees + 'deg)',
'-moz-transform' : 'rotate(' + degrees + 'deg)',
'-ms-transform' : 'rotate(' + degrees + 'deg)',
'transform' : 'rotate(' + degrees + 'deg)'
});
return $(this);
};
var rotation = 0;
var isDragging = false;
body {
height: 100%;
background-color: #e4e4e4;
}
#firstID {
width: 100px;
height: 100px;
margin: 0 auto;
border-style: groove;
background-color: #d1f1fc;
color: black;
position: relative;
text-align: center;
z-index: 99;
}
#secondID {
width: 100px;
height: 50px;
margin: 0 auto;
color: black;
border-style: groove;
position: relative;
background-color: #d1f1fc;
text-align: center;
z-index: 99;
}
#center {
width: 600px;
height: 300px;
position: relative;
margin: 0 auto;
border-style: groove;
background-color: #c0c0c0;
}
/* If in mobile screen with maximum width 479px. The iPhone screen resolution is 320x480 px (except iPhone4, 640x960) */
#media only screen and (max-width: 479px) {
#center{
width: 50%;
height : 50%;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<html>
<head>
<link rel="stylesheet" type="text/css" href="drag.css">
</head>
<body>
<input type="text" id="xPosition" value="x:0">
<input type="text" id="yPosition" value="y:0">
<div id="firstID" >MJ</div>
<div id="secondID" >MJ</div>
<div id="center">
</div>
</body>
</html>
The code is not finished, so the logic works with only a square, not rectangle.
What i need is to drag square element into the container on the page and make it stay inside it, with no possibility to leave it. That works fine in Mozilla, but in Chrome, i have problems with dragging the square after entering container. When i click on it and try to drag it, it moves far to the right side of the page. Why is that happening only in Chrome?