Javascript pan and zoom - javascript

I have a bunch of CSS and SVG elements that get placed into a div and I would like the user to be able to pan and zoom around to all of them. I am using the library from Jquery Panzoom. Is there a way to contain it so that if the user has an element outside of the viewport they will be able to pan to the edge of the container with all of the elements inside of it?
Below I will provide an example of what it's doing now. When it is all zoomed in and there is still an element not seen, I would like to be able to pan to the other element.
When Everything is seen, I would like it to not be able to pan but still zoom.
When it is zoomed all of the ways out, I would like the container to be the width of the viewport. The problem I am having is that if I set the container to be the width of the viewport at its most zoomed out level. It decides to hide below the viewport.
Example
Example using Panzoom
Code from codepen
Html
<div id="chart_container">
<div class="flowchart-example-container" id="example"></div>
</div>
<div class="draggable_operators">
<div class="draggable_operators_label">
Operators (drag and drop them in the flowchart):
</div>
<div class="draggable_operators_divs">
<div class="draggable_operator" data-nb-inputs="1" data-nb-outputs="0">1 input</div>
<div class="draggable_operator" data-nb-inputs="0" data-nb-outputs="1">1 output</div>
<div class="draggable_operator" data-nb-inputs="1" data-nb-outputs="1">1 input & 1 output</div>
<div class="draggable_operator" data-nb-inputs="1" data-nb-outputs="2">1 in & 2 out</div>
<div class="draggable_operator" data-nb-inputs="2" data-nb-outputs="1">2 in & 1 out</div>
<div class="draggable_operator" data-nb-inputs="2" data-nb-outputs="2">2 in & 2 out</div>
</div>
</div>
<button class="delete_selected_button">Delete selected operator / link</button>
CSS
body {
font-family: 'Helvetica Neue', Helvetica, Arial, serif;
font-size: 15px;
font-weight: 400;
line-height: 1.5;
color: #666;
}
#chart_container {
width: 100%;
height: 500px;
overflow: hidden;
background: repeating-linear-gradient( 45deg, #eee, #eee 10px, #e5e5e5 10px, #e5e5e5 20px);
border: 1px solid black;
}
.flowchart-example-container {
height: 200px;
border: 1px solid #BBB;
margin-bottom: 10px;
}
#example {
width: 2000px;
height: 2000px;
background: white;
}
.draggable_operators_label {
margin-bottom: 5px;
}
.draggable_operators_divs {
margin-bottom: 20px;
}
.draggable_operator {
display: inline-block;
padding: 2px 5px 2px 5px;
border: 1px solid #ccc;
cursor: grab;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Javascript
$(document).ready(function() {
var $flowchart = $('#example');
var $container = $flowchart.parent();
var cx = $flowchart.width() / 2;
var cy = $flowchart.height() / 2;
// Panzoom initialization...
$flowchart.panzoom();
// Centering panzoom
$flowchart.panzoom('pan', -cx + $container.width() / 2, -cy + $container.height() / 2);
// Panzoom zoom handling...
var possibleZooms = [0.5, 0.75, 1, 2, 3];
var currentZoom = 2;
$container.on('mousewheel.focal', function(e) {
e.preventDefault();
var delta = (e.delta || e.originalEvent.wheelDelta) || e.originalEvent.detail;
var zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
currentZoom = Math.max(0, Math.min(possibleZooms.length - 1, (currentZoom + (zoomOut * 2 - 1))));
$flowchart.flowchart('setPositionRatio', possibleZooms[currentZoom]);
$flowchart.panzoom('zoom', possibleZooms[currentZoom], {
animate: false,
focal: e
});
});
var data = {
operators: {
operator1: {
top: cy - 100,
left: cx - 200,
properties: {
title: 'Operator 1',
inputs: {},
outputs: {
output_1: {
label: 'Output',
}
}
}
},
operator2: {
top: cy,
left: cx + 140,
properties: {
title: 'Operator 2',
inputs: {
input_1: {
label: 'Input 1',
},
input_2: {
label: 'Input 2',
},
},
outputs: {}
}
},
},
links: {
link_1: {
fromOperator: 'operator1',
fromConnector: 'output_1',
toOperator: 'operator2',
toConnector: 'input_2',
},
}
};
// Apply the plugin on a standard, empty div...
$flowchart.flowchart({
data: data,
linkWidth: 5
});
$flowchart.parent().siblings('.delete_selected_button').click(function() {
$flowchart.flowchart('deleteSelected');
});
var $draggableOperators = $('.draggable_operator');
function getOperatorData($element) {
var nbInputs = parseInt($element.data('nb-inputs'));
var nbOutputs = parseInt($element.data('nb-outputs'));
var data = {
properties: {
title: $element.text(),
inputs: {},
outputs: {}
}
};
var i = 0;
for (i = 0; i < nbInputs; i++) {
data.properties.inputs['input_' + i] = {
label: 'Input ' + (i + 1)
};
}
for (i = 0; i < nbOutputs; i++) {
data.properties.outputs['output_' + i] = {
label: 'Output ' + (i + 1)
};
}
return data;
}
var operatorId = 0;
$draggableOperators.draggable({
cursor: "move",
opacity: 0.7,
helper: 'clone',
appendTo: 'body',
zIndex: 1000,
helper: function(e) {
var $this = $(this);
var data = getOperatorData($this);
return $flowchart.flowchart('getOperatorElement', data);
},
stop: function(e, ui) {
var $this = $(this);
var elOffset = ui.offset;
var containerOffset = $container.offset();
if (elOffset.left > containerOffset.left &&
elOffset.top > containerOffset.top &&
elOffset.left < containerOffset.left + $container.width() &&
elOffset.top < containerOffset.top + $container.height()) {
var flowchartOffset = $flowchart.offset();
var relativeLeft = elOffset.left - flowchartOffset.left;
var relativeTop = elOffset.top - flowchartOffset.top;
var positionRatio = $flowchart.flowchart('getPositionRatio');
relativeLeft /= positionRatio;
relativeTop /= positionRatio;
var data = getOperatorData($this);
data.left = relativeLeft;
data.top = relativeTop;
$flowchart.flowchart('addOperator', data);
}
}
});
});

Related

Why the div is not moving leftwards using "right" ? in third step

Here, I have code a program that revolves the targeted div in a rectangular path using CSS positions properties in setInterval() method.
Firstly, the div indeed moves rightwards (using left CSS property) and then downwards (using top CSS property) but when the thirds step comes it doesn't move leftwards (using CSS right property). Why is that so?
let a = 0;
let node = document.querySelector(".node");
let inter1 = setInterval(function() {
if (a == 260) {
clearInterval(inter1);
a = 0;
let inter2 = setInterval(function() {
if (a == 639) {
clearInterval(inter2);
a = 260;
let inter3 = setInterval(function() {
if (a == 0) {
clearInterval(inter3);
} else {
a -= 1;
node.style.right = a + "px";
}
}, 1)
} else {
a += 1;
node.style.top = a + "px";
}
}, 1)
} else {
a += 1;
node.style.left = a + "px";
}
}, 1)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: serif;
}
body {
background: black;
color: white;
width: 100vw;
}
.node {
background: dodgerblue;
color: white;
display: inline-block;
width: 100px;
height: 100px;
position: absolute;
}
<div class="node"></div>
Like the user #JavaScript wrote, you should decide for one - left or right.
Therefor you could change right to left in the inter3 statement:
let inter3 = setInterval(function() {
if (a == 0) {
clearInterval(inter3);
} else {
a -= 1;
node.style.left = a + "px";
}
}, 1);
let a = 0;
let node = document.querySelector(".node");
let inter1 = setInterval(function() {
if (a == 260) {
clearInterval(inter1);
a = 0;
let inter2 = setInterval(function() {
if (a == 639) {
clearInterval(inter2);
a = 260;
let inter3 = setInterval(function() {
if (a == 0) {
clearInterval(inter3);
} else {
a -= 1;
node.style.left = a + "px";
}
}, 1)
} else {
a += 1;
node.style.top = a + "px";
}
}, 1)
} else {
a += 1;
node.style.left = a + "px";
}
}, 1)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: serif;
}
body {
background: black;
color: white;
width: 100vw;
}
.node {
background: dodgerblue;
color: white;
display: inline-block;
width: 100px;
height: 100px;
position: absolute;
}
<div class="node"></div>

New data is being placed on top of old data (JavaScript)

I'm a newbie to the web development world, so please pardon me if I miss something here and there or if I describe the issue incorrectly.
GIF illustrating the issue
Basically, I copied different code from TradingView's two or more chart examples into one JSFiddle and tried to make a chart that could show candlesticks of different time frames with the symbol name, OHLCV, MA10 values. Everything works fine when the chart loads for the first time but when click the button to change the time frame, all the candles and MA10 line loads fine but the OHLCV and MA10 values seem like being put on the old (initially loaded) data.
When I tried it for the first time all the candles and MA10 lines were also overlapping then I figured that the whole chart has to be removed so that MA10's values will be re-calculated. So now the candlesticks and MA10 are loading fine but OHLCV values are still overlapping on the previous values. Since I'm new to JS I can't figure out the right keywords for my problem so I'm not getting results that address my problem.
Could somebody help me to figure out what's going wrong?
Thank you.
I had to remove most of historic data to comply with Stackoverflow's limit of 30000 chars per post, so snippet below will not work propely. You can take a look at the code here https://jsfiddle.net/akshay7892/dhutrgfn/7/
function createSimpleSwitcher(items, activeItem, activeItemChangedCallback) {
var switcherElement = document.createElement('div');
switcherElement.classList.add('switcher');
var intervalElements = items.map(function(item) {
var itemEl = document.createElement('button');
itemEl.innerText = item;
itemEl.classList.add('switcher-item');
itemEl.classList.toggle('switcher-active-item', item === activeItem);
itemEl.addEventListener('click', function() {
onItemClicked(item);
});
switcherElement.appendChild(itemEl);
return itemEl;
});
function onItemClicked(item) {
if (item === activeItem) {
return;
}
intervalElements.forEach(function(element, index) {
element.classList.toggle('switcher-active-item', items[index] === item);
});
activeItem = item;
activeItemChangedCallback(item);
}
return switcherElement;
}
function calculateSMA(data, count){
var avg = function(data) {
var sum = 0;
for (var i = 0; i < data.length; i++) {
sum += data[i].close;
}
return sum / data.length;
};
var result = [];
for (var i=count - 1, len=data.length; i < len; i++){
var val = avg(data.slice(i - count + 1, i));
result.push({ time: data[i].time, value: val});
}
//console.log(result)
return result;
}
function calopen(data){
var open = [];
for (var i = 0; i < data.length; i++) {
open.push({ time: data[i].time, value: data[i].open});
}
return open;
}
//console.log(calopen(data))
function calhigh(data){
var high = [];
for (var i = 0; i < data.length; i++) {
high.push({ time: data[i].time, value: data[i].high});
}
return high;
}
function callow(data){
var low = [];
for (var i = 0; i < data.length; i++) {
low.push({ time: data[i].time, value: data[i].low});
}
return low;
}
function calclose(data){
var close = [];
for (var i = 0; i < data.length; i++) {
close.push({ time: data[i].time, value: data[i].close});
}
return close;
}
function calv(data){
var vol = [];
for (var i = 0; i < data.length; i++) {
vol.push({ time: data[i].time, value: data[i].volume});
}
return vol;
}
var one_minData = [{"time":1650350460,"open":329.35,"high":329.45,"low":329.1,"close":329.45,"volume":9795},{"time":1650350520,"open":329.45,"high":329.5,"low":329.15,"close":329.15,"volume":20626},{"time":1650350580,"open":329.15,"high":329.45,"low":329.15,"close":329.45,"volume":8762},{"time":1650350640,"open":329.5,"high":330.2,"low":329.25,"close":330.05,"volume":56546},{"time":1650350700,"open":330.15,"high":330.15,"low":329.55,"close":329.8,"volume":23489},{"time":1650350760,"open":329.8,"high":329.9,"low":329.6,"close":329.75,"volume":20630},]
var fifteen_minData = [{"time":1576728900,"open":509.8,"high":512.6,"low":508.9,"close":511.05,"volume":210630},{"time":1576729800,"open":511.0,"high":511.5,"low":509.35,"close":510.25,"volume":109974},{"time":1576730700,"open":510.25,"high":510.25,"low":506.2,"close":507.65,"volume":177816},]
var dayData = [{"time":1564963200,"open":460.0,"high":475.9,"low":455.85,"close":471.95,"volume":1465110},{"time":1565049600,"open":472.0,"high":487.5,"low":468.25,"close":482.15,"volume":795823},{"time":1565136000,"open":484.0,"high":489.95,"low":474.0,"close":477.25,"volume":312625},]
var weekData =
[{"time":1529884800,"open":538.35,"high":542.0,"low":508.0,"close":526.55,"volume":590970},{"time":1530489600,"open":530.0,"high":567.8,"low":523.0,"close":544.3,"volume":550127},{"time":1531094400,"open":548.8,"high":588.8,"low":544.5,"close":568.95,"volume":1021330},{"time":1531699200,"open":558.25,"high":706.8,"low":544.55,"close":687.4,"volume":3131754}{"time":1566777600,"open":484.0,"high":497.65,"low":455.15,"close":469.6,"volume":775278},];
var intervals = ['1','15','1D', '1W'];
var seriesesData = new Map([
['1', one_minData ],
['15', fifteen_minData ],
['1D', dayData ],
['1W', weekData ],
]);
var switcherElement = createSimpleSwitcher(intervals, intervals[0], syncToInterval);
document.body.style.position = 'relative';
var container = document.createElement('div');
document.body.appendChild(container);
document.body.appendChild(switcherElement);
var width = 600;
var height = 300;
var candleSeries = null;
function syncToInterval(interval) {
if (candleSeries) {
chart.remove();
chart.remove(legend);
//req_data = null;
}
chart = LightweightCharts.createChart(container, {
width: width,
height: height,
crosshair: {
mode: LightweightCharts.CrosshairMode.Normal,
},
timeScale: {
borderVisible: true,
timeVisible: true,
secondsVisible: false,},
});
candleSeries = chart.addCandlestickSeries();
req_data = seriesesData.get(interval)
//console.log(data);
candleSeries.setData(req_data);
////charts time, volume, color data////////
var smaLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
lineWidth: 2,
});
var openLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
visible: false,
lineWidth:0,
});
var highLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
visible: false,
lineWidth:0,
});
var lowLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
visible: false,
lineWidth:0,
});
var closeLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
visible: false,
lineWidth:0,
});
var volumeSeries = chart.addHistogramSeries({
color: '#26a69a',
priceFormat: {
type: 'volume',
},
priceScaleId: '',
scaleMargins: {
top: 0.8,
bottom: 0,
},
});
var volLine = chart.addLineSeries({
color: 'rgba(4, 111, 232, 1)',
visible: false,
lineWidth:5,
});
function setLegend6Text(fysymbol) {
legend6.innerHTML = '<span style=style="font-size: 24px; margin: 4px 0px; color: #20262E">' + fysymbol + '</span>';
//console.log("name",fysymbol);
}
function setLegendText(priceValue) {
let val = 'n/a';
if (priceValue !== undefined) {
val = (Math.round(priceValue * 100) / 100).toFixed(2);
}
legend.innerHTML = '<br/><br/>MA10: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + val + '</span>';
}
function setLegendText1(openValue) {
let openVal = 'n/a';
if (openValue !== undefined) {
openVal = (Math.round(openValue * 100) / 100).toFixed(2);
}
legend1.innerHTML = '<br/>O: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + openVal + '</span>';
}
function setLegendText2(highValue) {
let highVal = 'n/a';
if (highValue !== undefined) {
highVal = (Math.round(highValue * 100) / 100).toFixed(2);
}
legend2.innerHTML = '<br/>H: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + highVal + '</span>';
}
function setLegendText3(lowValue) {
let lowVal = 'n/a';
if (lowValue !== undefined) {
lowVal = (Math.round(lowValue * 100) / 100).toFixed(2);
}
legend3.innerHTML = '<br/>L: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + lowVal + '</span>';
}
function setLegendText4(closeValue) {
let closeVal = 'n/a';
if (closeValue !== undefined) {
closeVal = (Math.round(closeValue * 100) / 100).toFixed(2);
}
legend4.innerHTML = '<br/>C: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + closeVal + '</span>';
}
function setLegendText5(volValue) {
let volVal = 'n/a';
if (volValue !== undefined) {
volVal = (Math.round(volValue * 100) / 100).toFixed(2);
}
legend5.innerHTML = '<br/>V: <span style="color:rgba(4, 111, 232, 1); margin: 4px 0px;">' + (volVal/100000) + 'L' + '</span>';
}
//console.log(lowdata)
var legend = document.createElement('div');
legend.className = 'sma-legend';
container.appendChild(legend);
legend.style.display = 'block';
legend.style.left = 3 + 'px';
legend.style.top = 20 + 'px';
var legend1 = document.createElement('div');
legend1.className = 'OHLC-legend';
container.appendChild(legend1);
legend1.style.display = 'block';
legend1.style.left = 3 + 'px';
legend1.style.top = 10 + 'px';
var legend2 = document.createElement('div');
legend2.className = 'OHLC-legend';
container.appendChild(legend2);
legend2.style.display = 'block';
legend2.style.left = 78 + 'px';
legend2.style.top = 10 + 'px';
var legend3 = document.createElement('div');
legend3.className = 'OHLC-legend';
container.appendChild(legend3);
legend3.style.display = 'block';
legend3.style.left = 153 + 'px';
legend3.style.top = 10 + 'px';
var legend4 = document.createElement('div');
legend4.className = 'OHLC-legend';
container.appendChild(legend4);
legend4.style.display = 'block';
legend4.style.left = 228 + 'px';
legend4.style.top = 10 + 'px';
var legend5 = document.createElement('div');
legend5.className = 'V-legend';
container.appendChild(legend5);
legend5.style.display = 'block';
legend5.style.left = 303 + 'px';
legend5.style.top = 10 + 'px';
var legend6 = document.createElement('div');
legend6.className = 'fysymbol-legend';
container.appendChild(legend6);
legend6.style.display = 'block';
legend6.style.left = 3 + 'px';
legend6.style.top = 0 + 'px';
var fysymbol = "NSE:SBIN-EQ";
voldata = calv(req_data)
volumeSeries.setData(voldata)
volLine.setData(voldata);
smaData = calculateSMA(req_data, 10);
smaLine.setData(smaData);
opendata = calopen(req_data)
openLine.setData(opendata);
highdata = calhigh(req_data)
highLine.setData(highdata);
lowdata = callow(req_data)
lowLine.setData(lowdata);
closedata = calclose(req_data)
closeLine.setData(closedata);
var datesForMarkers = [req_data[req_data.length - 39], req_data[req_data.length - 19]];
var indexOfMinPrice = 0;
for (var i = 1; i < datesForMarkers.length; i++) {
if (datesForMarkers[i].high < datesForMarkers[indexOfMinPrice].high) {
indexOfMinPrice = i;
}
}
var markers = [{ time: req_data[req_data.length - 48].time, position: 'aboveBar', color: '#f68410', shape: 'circle', text: 'D' }];
for (var i = 0; i < datesForMarkers.length; i++) {
if (i !== indexOfMinPrice) {
markers.push({ time: datesForMarkers[i].time, position: 'aboveBar', color: '#e91e63', shape: 'arrowDown', text: 'Sell # ' + Math.floor(datesForMarkers[i].high - 1) });
} else {
markers.push({ time: datesForMarkers[i].time, position: 'belowBar', color: '#2196F3', shape: 'arrowUp', text: 'Buy # ' + Math.floor(datesForMarkers[i].low + 1) });
}
}
candleSeries.setMarkers(markers);
setLegendText(smaData[smaData.length - 1].value);
setLegendText1(opendata[opendata.length -1 ].value);
setLegendText2(highdata[highdata.length -1 ].value);
setLegendText3(lowdata[lowdata.length -1 ].value);
setLegendText4(closedata[closedata.length -1 ].value);
setLegendText5(voldata[voldata.length -1 ].value);
setLegend6Text(fysymbol);
chart.subscribeCrosshairMove((param) => {
setLegendText(param.seriesPrices.get(smaLine)),
setLegendText1(param.seriesPrices.get(openLine)),
setLegendText2(param.seriesPrices.get(highLine)),
setLegendText3(param.seriesPrices.get(lowLine)),
setLegendText4(param.seriesPrices.get(closeLine)),
setLegendText5(param.seriesPrices.get(volLine)),
setLegend6Text(fysymbol);
});
}
syncToInterval(intervals[0]);
html,
body {
font-family: 'Trebuchet MS', Roboto, Ubuntu, sans-serif;
background: #f9fafb;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.switcher {
display: flex;
align-items: center;
height: 30px;
margin-top:8px;
color: #2196F3;
}
.switcher-item {
cursor: pointer;
text-decoration: none;
display: inline-block;
padding: 6px 8px;
font-size: 14px;
color: #262b3e;
background-color: transparent;
margin-right: 8px;
border: none;
border-radius: 4px;
outline: none;
}
.switcher-item:hover {
background-color: #f2f3f5;
}
.switcher-active-item {
text-decoration: none;
cursor: default;
color: #262b3e;
}
.switcher-active-item,
.switcher-active-item:hover {
background-color: #e1eff9;
}
.sma-legend {
width: 96px;
height: 10px;
position: absolute;
padding: 8px;
font-size: 14px;
background-color: rgba(255, 255, 255, 0.23);
text-align: left;
z-index: 1000;
pointer-events: none;
white-space: normal;
}
.OHLC-legend {
width: 96px;
height: 10px;
position: absolute;
padding: 8px;
font-size: 16px;
background-color: rgba(255, 255, 255, 0.23);
text-align: left;
z-index: 1000;
pointer-events: none;
white-space: normal;
}
.V-legend {
width: 120px;
height: 70px;
position: absolute;
padding: 8px;
font-size: 16px;
background-color: rgba(255, 255, 255, 0.23);
text-align: left;
z-index: 1000;
pointer-events: none;
white-space: normal;
}
.fysymbol-legend {
width: 250px;
height: 70px;
position: absolute;
padding: 8px;
/*font-size: 20px;*/
background-color: rgba(255, 255, 255, 0.23);
text-align: left;
z-index: 1000;
pointer-events: none;
white-space: normal
}
<script src="https://unpkg.com/lightweight-charts#3.4.0/dist/lightweight-charts.standalone.production.js"></script>
First thing to look at when something's not working as expected (whatever your level!) is the dev console!
Here you can clearly see that each time you click on your button you're not only replacing an "old" value but instead adding one on top of the previous one(s).
I would recommend having an init function and/or components and an update function.

Opposing condition for a div

I need to make a ball grow until it reaches 400, which thereafter it would shrink the same way. Would really appreciate feedback what I'm doing wrong in my code:
var
ball1Size = 100
, ball2Size = 100
, ball2SizeStep = 50
;
function onBall2Click()
{
var ball2 = document.querySelector('.ball2');
ball2Size = ball2Size + 50;
if (ball2Size > 400) {
ball2Size = ball2Size - 100;
}
else {
ball2Size - 150;
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size;
ball2.style.height = ball2Size;
}
body {
background-color : black;
text-align : center;
}
h1 {
color : white;
}
div {
width : 100px;
height : 100px;
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
}
.ball2 {
background-color : orange;
}
<div class="ball2" onclick="onBall2Click()">
SIZE
</div>
See code fix and syntax fixes
here:
var ball1Size = 100;
var ball2Size = 100;
var ball2SizeStep = 50;
function onBall2Click() {
var ball2 = document.querySelector('.ball2');
ball2Size = ball2Size + ball2SizeStep;
if (ball2Size >= 400) {
ball2SizeStep = -50
} else if (ball2Size <= 50) {
ball2SizeStep = 50
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size + "px";
ball2.style.height = ball2Size + "px";
}
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
div {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div class="ball2" onclick="onBall2Click()" >
SIZE
</div>
This is what you want to do, increase the balls size for each click that it can be increased for, i.e., is under 400, when it reaches 400, decrease the ball size for each click until it reaches its original size. Rinse and repeat.
var ball1Size = 100;
var ball2Size = 100;
var ball2SizeStep = 50;
let isBallDecreasing = false;
function onBall2Click() {
var ball2 = document.querySelector('.ball2');
if (ball2Size === 100) isBallDecreasing = false;
if (ball2Size >= 400 || isBallDecreasing) {
ball2Size -= 50;
isBallDecreasing = true;
} else {
ball2Size += 50
isBallDecreasing = false;
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size;
ball2.style.height = ball2Size
}
<div class="ball2" onclick="onBall2Click()">
SIZE
</div>
Using a boolean to track the direction can simplify it
let direction = 1;
const min = 100;
const max = 400;
const step = 50;
let current = 100;
var ball = document.querySelector(".ball2");
ball.addEventListener("click", function() {
current += direction * step;
if (current >= max) {
current = max;
direction = -1;
} else if (current <= min) {
current = min;
direction = 1;
}
updateElem();
});
function updateElem() {
ball.textContent = current;
ball.style.width = current + 'px';
ball.style.height = current + 'px';
}
updateElem();
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
.ball {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div class="ball ball2">
SIZE
</div>
If you are going to have more than one of these you probably want to have some sort of way to keep track. Using data attributes can help out so you do not need more than one copy of the code.
document.querySelectorAll(".ball2").forEach(function (ball) {
ball.addEventListener("click", function () {
let direction = +ball.dataset.direction;
const min = +ball.dataset.min;
const max = +ball.dataset.max;
const step = +ball.dataset.step;
const current = +ball.dataset.current;
let updated = current + direction * step;
if (updated >= max) {
updated = max;
direction = -1;
} else if (updated <=min) {
updated = min;
direction = 1;
}
updateElem();
ball.dataset.direction = direction;
ball.dataset.current = updated;
});
function updateElem() {
const num = ball.dataset.current;
ball.textContent = num;
ball.style.width = num + 'px';
ball.style.height = num + 'px';
}
updateElem();
});
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
.ball {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div
class="ball ball2"
data-direction="1"
data-min="100"
data-max="400"
data-step="50"
data-current="100">
SIZE
</div>
<div
class="ball ball2"
data-direction="-1"
data-min="100"
data-max="400"
data-step="50"
data-current="400">
SIZE
</div>
<div
class="ball ball2"
data-direction="1"
data-min="0"
data-max="1000"
data-step="100"
data-current="200">
SIZE
</div>
you can also Use CSS custom properties for simplifying your code:
const ball2 = document.querySelector('.ball2');
var
ballSz = 100
, ball2SizeStep = 50
;
ball2.onclick =()=>
{
ballSz += ball2SizeStep
ball2.textContent = ballSz
ball2.style.setProperty('--szHW', ballSz + 'px')
if (ballSz >= 400) ball2SizeStep = -50
if (ballSz <= 100) ball2SizeStep = +50
}
body {
background-color : black;
text-align : center;
}
h1 {
color : white;
}
div {
--szHW : 100px; /* <--- CSS custom properties */
width : var(--szHW);
height : var(--szHW);
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
}
.ball2 {
background-color : orange;
}
<div class="ball2">
SIZE
</div>
the same, for multiples balls
document.querySelectorAll('.ball2').forEach( ball2 =>
{
ball2.style.setProperty('--szHW', ball2.dataset.size + 'px') // init sizes according to data-size value
ball2.onclick =_=>
{
let
min = Number(ball2.dataset.min)
, max = Number(ball2.dataset.max)
, step = Number(ball2.dataset.step)
, size = Number(ball2.dataset.size)
;
if ( size === min && step < 0
|| size === max && step > 0 )
{
step *= -1
ball2.dataset.step = step
}
ball2.dataset.size = size += step
ball2.style.setProperty('--szHW', size + 'px')
}
})
body {
background-color : black;
text-align : center;
}
div.ball2 {
--szHW : 100px; /* <--- CSS custom properties */
width : var(--szHW);
height : var(--szHW);
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
background : orange;
}
div.ball2::before {
content: attr(data-size)
}
<div class="ball2" data-min="100" data-max="400" data-step="50" data-size="100"></div>
<div class="ball2" data-min="100" data-max="400" data-step="50" data-size="400"></div>
<div class="ball2" data-min="100" data-max="1000" data-step="100" data-size="200"></div>

Collision detection using pure jQuery is not giving desired output

I am trying to develop a very simple game where the ship (red box) will move left-right when user clicks on playground.
There are some moving walls (black boxes) as obstacles that the ship should avoid colliding with.
If any collision happens, the walls will stop moving and a text will be printed out in console.
I have succeeded to get this as close as I can. But its working sometime, not always. You can see it in the code below, try to collide with wall. Sometime it will stop them and print text, sometime it will just ignore the collision as if nothing happens.
I have no clue why this is happening.
Here is the code.
$('document').ready(function() {
var $totalHeight = $('.inner').height(); //of walls
var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height
$('.wall').each(function(i, obj) {
$(this).height($maxHeight);
$('.wall.four').css({
'height': $wallGap
});
})
var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
var $wallOneTop = 0;
var $wallTwoTop = $maxHeight + $wallGap;
var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
var $wallFourTop = -$('.wall.four').height() - $wallGap;
$('.wall.one').css({
'top': $wallOneTop
});
$('.wall.two').css({
'top': $wallTwoTop
});
$('.wall.three').css({
'top': $wallThreeTop
});
$('.wall.four').css({
'top': $wallFourTop
});
function moveWall(wallObj) {
var $currentTop = wallObj.position().top;
var $limitTop = $('.inner').height();
if ($currentTop >= $limitTop) {
var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
wallObj.height($rand);
var $top = -(wallObj.height());
} else {
var $top = (wallObj.position().top) + 5;
}
var $collide = checkCollision(wallObj);
wallObj.css({
'top': $top
});
return $collide;
}
var $wallTimer = setInterval(function() {
$('.wall').each(function(i, obj) {
var $status = moveWall($(this));
if ($status == true) {
clearInterval($wallTimer);
}
})
}, 40);
function checkCollision(wallObj) {
var $ship = $('.ship');
var $shipWidth = $ship.width();
var $shipHeight = $ship.height();
var $shipLeft = $ship.position().left;
var $shipRight = $shipLeft + $shipWidth;
var $shipTop = $ship.position().top;
var $shipBottom = $shipTop + $shipHeight;
var $wall = wallObj;
var $wallWidth = wallObj.width();
var $wallHeight = wallObj.height();
var $wallLeft = wallObj.position().left;
var $wallRight = $wallLeft + $wallWidth;
var $wallTop = wallObj.position().top;
var $wallBottom = $wallTop + $wallHeight;
if (
$shipLeft >= $wallRight ||
$shipRight <= $wallLeft ||
$shipTop >= $wallBottom ||
$shipBottom <= $wallTop
) {
return false;
} else {
console.log("dhumm!");
return true;
}
}
$('.outer .inner').click(function() {
var $ship;
$ship = $('.ship');
$shipLeft = $ship.position().left;
$shipRight = $shipLeft + $ship.width();
$inner = $('.inner');
$innerLeft = $inner.position().left;
$innerRight = $innerLeft + $inner.width();
if (($shipLeft < $inner.width() - $ship.width())) {
$ship.animate({
"left": $inner.width() - $ship.width()
}, 500, "linear");
} else if (($shipRight >= $inner.width())) {
$ship.animate({
"left": '0'
}, 500, "linear");
}
});
});
.outer {
background: #fff;
border: 20px solid #efefef;
width: 400px;
height: 600px;
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
}
.outer .inner {
background: #fff;
height: 100%;
width: 100%;
margin: auto;
position: relative;
overflow: hidden;
}
.outer .inner .wall {
width: 5px;
position: absolute;
left: 50%;
transform: translateX(-50%);
background: #000;
}
.outer .inner .ship {
width: 15px;
height: 15px;
background: red;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="wall one"></div>
<div class="wall two"></div>
<div class="wall three"></div>
<div class="wall four"></div>
<div class="ship"></div>
</div>
</div>
As freefomn-m already said.
Check for collision in the animation cycle of the ship, not the walls.
For this I use the second type of parameters for jQuery's .animate method
.animate( properties, options )
I use the "progress" option to check the collision in every movement cycle of the ship.
console.clear();
$('document').ready(function() {
var collided = false;
var collidedWith = null;
var $ship = $('.ship');
var $walls = $('.wall')
var $totalHeight = $('.inner').height(); //of walls
var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height
$('.wall').each(function(i, obj) {
$(this).height($maxHeight);
$('.wall.four').css({
'height': $wallGap
});
})
var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
var $wallOneTop = 0;
var $wallTwoTop = $maxHeight + $wallGap;
var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
var $wallFourTop = -$('.wall.four').height() - $wallGap;
$('.wall.one').css({
'top': $wallOneTop
});
$('.wall.two').css({
'top': $wallTwoTop
});
$('.wall.three').css({
'top': $wallThreeTop
});
$('.wall.four').css({
'top': $wallFourTop
});
function moveWall(wallObj) {
var $currentTop = wallObj.position().top;
var $limitTop = $('.inner').height();
if ($currentTop >= $limitTop) {
var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
wallObj.height($rand);
var $top = -(wallObj.height());
} else {
var $top = (wallObj.position().top) + 5;
}
// var $collide = checkCollision(wallObj);
wallObj.css({
'top': $top
});
// return $collide;
}
var $wallTimer = setInterval(function() {
$walls.each(function(i, obj) {
moveWall($(this));
if (collided) {
clearInterval($wallTimer);
}
})
}, 40);
function checkCollision() {
var $shipWidth = $ship.width();
var $shipHeight = $ship.height();
var $shipLeft = $ship.position().left;
var $shipRight = $shipLeft + $shipWidth;
var $shipTop = $ship.position().top;
var $shipBottom = $shipTop + $shipHeight;
$('.wall').each(function(i) {
var $wall = $(this);
var $wallWidth = $wall.width();
var $wallHeight = $wall.height();
var $wallLeft = $wall.position().left;
var $wallRight = $wallLeft + $wallWidth;
var $wallTop = $wall.position().top;
var $wallBottom = $wallTop + $wallHeight;
if (
$shipLeft < $wallRight &&
$shipRight > $wallLeft &&
$shipTop < $wallBottom &&
$shipBottom > $wallTop
) {
console.log("dhumm!");
collided = true;
collidedWith = $wall
$wall.addClass('crashed')
$ship.addClass('crashed')
$ship.stop();
return false;
}
})
}
$('.outer .inner').click(function() {
var $ship;
$ship = $('.ship');
$shipLeft = $ship.position().left;
$shipRight = $shipLeft + $ship.width();
$inner = $('.inner');
$innerLeft = $inner.position().left;
$innerRight = $innerLeft + $inner.width();
if (($shipLeft < $inner.width() - $ship.width())) {
$ship.animate({
"left": $inner.width() - $ship.width()
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
} else if (($shipRight >= $inner.width())) {
$ship.animate({
"left": '0'
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
}
});
});
.outer {
background: #fff;
border: 20px solid #efefef;
width: 400px;
height: 600px;
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
}
.outer .inner {
background: #fff;
height: 100%;
width: 100%;
margin: auto;
position: relative;
overflow: hidden;
}
.outer .inner .wall {
width: 5px;
position: absolute;
left: 50%;
transform: translateX(-50%);
background: #000;
}
.outer .inner .wall.crashed {
background: red;
}
.outer .inner .ship {
width: 15px;
height: 15px;
background: orange;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.outer .inner .ship.crashed {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="wall one"></div>
<div class="wall two"></div>
<div class="wall three"></div>
<div class="wall four"></div>
<div class="ship"></div>
</div>
</div>
As a recommendation how I would do this from scratch.
Use an update cycle that is called by either setInterval or setTimeout, or even better with requestAnimationFrame. The updatecycle would be responsible for the time progress and orchestrate the different objects. The structure would be like this.
jQuery(function($) { // same as $('document').ready()
var ship = ...;
var boundaries = ...;
var walls = ...;
var clickEvents = [];
document.addEventListener('click', function(e) {clickEvents.push(e)})
var handleEvents = function() {}
var setupWalls = function () {}
var setupShip= function () {}
var moveWalls = function () {}
var moveShip = function () {}
var checkCollision() {}
var setup = function() {
setupWalls();
setupShip();
// set the initial positions of the ships and the walls
}
var update = function() {
handleEvents();
moveWalls();
moveShips();
var collided = checkCollision();
if (!collided) {
setTimeout(update, 30);
}
}
setup();
update();
})

Can not get value inside loop using pure JavaScript

Why I cannot get value inside the loop
Can use javascript only
Pause text for 2000ms on hover
reverse work on hover but should pause it first and then reverse the sequence until mouse out.
loop on reading text from textarea "input"
find this line in javascript to control delay
if(document.getElementById('flag2').value == "0"){
/*
1 : center
2 - 4 : center 2
5 - 7 : center 3
8 - 10 : center 4
11 - 13 : center 5
14 - 16 : center 6
17 - 19 : center 7
*/
stopNow= false;
function Loop(txt, wpm) {
txt = txt.replace(/\n/g, ' ');
var a = txt.split(' '),
n = a.length,
i = 0,
b = document.getElementById('bw'),
tw = document.getElementById('tw'),
tx = {
L: document.getElementById('txt_L'),
C: document.getElementById('txt_C'),
R: document.getElementById('txt_R')
}
;
function width(w) {
tw.textContent = w;
return tw.offsetWidth;
}
function middle(w) {
var x = 0;
var n = w.length,
// Center char calculation. 1=1, 2-4=2, 5-7=3, ...
c = ~~((n + 1) / 3) + 1,
z = {};
if (!n)
return;
z.a = width(w.substr(0, c - 1));
z.b = width(w.substr(0, c));
z.c = (z.b - z.a) / 2;
b.style.paddingLeft = ~~(110 - (z.a + z.c)) + 'px';
tx.L.textContent = w.substr(0, c - 1);
tx.C.textContent = w.substr(c - 1, 1);
tx.R.textContent = w.substr(c);
}
function word() {
if(stopNow){
middle(a[i])
if (--i === n)
i = 0;
return 0;
}
else{
middle(a[i])
if (++i === n)
i = 0;
return 0;
}
}
this.whiteout = function(w) {
if (w) {
tx.L.style.color = '#fff';
tx.R.style.color = '#fff';
} else {
tx.L.style.color = '#080';
tx.R.style.color = '#000';
}
};
wpm = wpm || 200;
var __time = 2000;
//can not get changes value here
if(document.getElementById('flag2').value == "0"){
__time = (60/wpm) * 1000;}
document.getElementById('flag1').innerHTML += document.getElementById('flag2').innerHTML ;
var tt = setInterval(function (){
document.getElementById('flag1').innerHTML = word();
}, __time);
}
var txt = document.getElementById('input').value;
var ww = new Loop(txt, 250);
document.getElementById('bw').addEventListener('change', function() {
ww.whiteout(this.checked);
});
document.getElementById('txtHolder').addEventListener('mouseover', function () {
stopNow = true;
document.getElementById('flag2').innerHTML = "1";
});
document.getElementById('txtHolder').addEventListener('mouseout', function () {
stopNow = false;
document.getElementById('flag2').innerHTML = "0";
});
* {
padding: 0;
margin:0;
}
.hide {
display: none;
}
#tw {
position: absolute;
visibility: hidden;
white-space: nowrap;
}
#tw,
#txt_L,
#txt_C,
#txt_R
{
font: bold 18px Arial;
line-height: 1;
}
#txt_L { color: #000; }
#txt_C { color: #f00; }
#txt_R { color: #000; }
#wrap {
position: relative;
width: 220px;
height: 50px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
#b1 {
width: 110px;
height: 50px;
border-right: 1px solid #000;
float:left;
}
#bw {
position: absolute;
width: 220px;
top: 10px;
background: #fff;
padding: 5px 0;
line-height: 1;
vertical-align: middle;
}
<!--
EXTRA POINTS---------
Pause text for 2000ms on hover and reverse the sequence until mouse out.
-->
<div id="flag1">
</div>
<div id="flag2">
</div>
<div id="txtHolder">
<span id="tw"></span>
<div id="wrap">
<div id="b1"></div>
<div id="bw"><span id="txt_L"> </span><span id="txt_C"></span><span id="txt_R"></span></div>
</div>
</div>
<textarea id="input" class="hide">
Oh, hey there, TIDWRTWHUFOO fans. Thinking about going for a little dip this summer? How about you go for a swim with a one-ton crab that will smash you under its massive legs? Sound fun? Definitely!
Crabster is a wild crab-like robot designed to explore the deepest oceans without smashing itself into a squashed tin can. It has massive legs, a large body, and lots of cameras so it can find you even if you’re the dude from The Big Blue and can dive really deep. There’s no escaping our future robotic-aquatic overlords
</textarea>

Categories