Recursively Draw a Box JavaScript - javascript

I am trying to draw a box inside of another the inner, and keep drawing them until the width is 2px
Here is the fiddle: http://jsfiddle.net/443wovkk/
JavaScript:
var innerBox = $('.box');
var innerBoxDimentions;
var boxHtml = innerBox.clone().removeClass('outter');
$(document).ready(function(){
do {
innerBox = findInnerBox(innerBox);
innerBoxDimentions = innerBox.width() / 2;
boxHtml = boxHtml.width(innerBoxDimentions).height(innerBoxDimentions);
innerBox.append(boxHtml);
}
while (innerBoxDimentions > 2);
//var boxHtml = innerBox.clone().width(innerBoxDimentions/2).height(innerBoxDimentions/2);
innerBox.append(boxHtml);
});
function findInnerBox(box){
if(box.children('.box').length > 0){
findInnerBox(box.children('.box'))
} else{
return box;
}
}
Currently it only draws one box. It should keep drawing more boxes inside the inner most box until the last box is 2px wide.
How do I recursively draw a box inside the inner most box continuously until the last box drawn reaches 2px wide?

There are two critical problems with your code keeping it from working.
1) You are always updating the same "box" variable. You need to create a different one each time. I fixed this by adding a call to clone().
2) The recursive function does not return a value after recursing. Add a return here in findInnerBox.
The following code does what you want with those minor adjustments:
var innerBox = $('.box');
var innerBoxDimentions;
var boxHtml = innerBox.clone().removeClass('outter');
$(document).ready(function(){
do {
innerBox = findInnerBox(innerBox);
innerBoxDimentions = innerBox.width() / 2;
boxHtml = boxHtml.clone().width(innerBoxDimentions).height(innerBoxDimentions);
innerBox.append(boxHtml);
}
while (innerBoxDimentions > 2);
//var boxHtml = innerBox.clone().width(innerBoxDimentions/2).height(innerBoxDimentions/2);
innerBox.append(boxHtml);
});
function findInnerBox(box){
if(box.children('.box').length > 0){
return findInnerBox(box.children('.box'))
} else{
return box;
}
}
/* CSS Styles for Recursive Box */
.box{ border:solid 1px #000; }
.outter { width: 500px; height:500px }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<div class="box outter"></div>

Opps, too late I guess.
Here is my version of the code
var size = 500;
while(size = size - 20){
$('.box:last').append('<div class="'+size+' box"></div>');
$('.'+size).css('height',size).css('width',size);
}
http://jsfiddle.net/443wovkk/2/

Related

Changing image size using input and buttons

I want to change the size of an image with an input and buttons.
I have an input, to which i insert the desired size, an image and 2 buttons.
One for making the image size change according to the input ( for example, if the user typed 300 in the input, the image width and height, will both change to 300px).
And one for making the image size double itself, by clicking the other button.
javascript :
var myImg = document.getElementById("myImg")
var input = document.getElementById("insert")
function increaseSize()
{
input.value = myImg.size.width
input.value = myImg.size.height
}
function doubleSize()
{
myImg.style.width * 2
myImg.style.height * 2
}
It didn't work.
You've got errors in your code. The first thing I can see is in the increaseSize() function, you are assigning the value to the input, not the image.
input.value = myImg.size.width
This line means that you have taken the width of the image and inserted that value into your input, which is the opposite of what you want to do. You want to take the value in your input and inject it into the images style.width property. So for starters, change that function (also there is no .size property, you wanted .style:
// option 1 create within function
function increaseSize() {
var myImg = document.getElementById( "myImg" );
myImg.style.width = input.value;
myImg.style.height = input.value;
}
// option 2 pass as parameters
var myImg = document.getElementById( "myImg" ); // assign the variables
var input = document.getElementById( "size" );
function increaseSize( img, input ) { // create the function
img.style.width = input.value + 'px'; // assign a unit type to it, as it is a css value
img.style.height = input.value + 'px';
}
increaseSize( myImg ); // run the function, passing in our variables
You have to use style to assign width and height using CSS, please find below example, where I have used the button to change the size, you can do it with input field as well:
document.getElementById("go").addEventListener("click", function() {
var img = document.querySelectorAll("#container .image img")[0];
img.style.height = "200px";
img.style.width = "200px";
});
div#container {
width: 100%;
height: 100%;
}
div.image img {
position:fixed;
width: 100px;
height: 100px;
}
<button id="go">Increase</button>
<div id="container">
<div class="image"><img src="http://libcom.org/files/images/library/black-square.jpg"/></div>
</div>
The width and height properties return string with px appended to it so remove the px part convert it to number and multiply it by 2.
var myImg = document.getElementById("myImg")
var input = document.getElementById("insert")
function increaseSize() {
myImg.style.width = input.value + 'px';
myImg.style.height = input.value + 'px';
}
function doubleSize() {
myImg.style.width = Number(myImg.style.width.slice(0,-2)) * 2 + 'px';
myImg.style.height = Number(myImg.style.height.slice(0,-2)) * 2 + 'px';
}
Try this
function changeSize(){
console.log('Clikced change size!');
var size=parseInt(document.querySelector('#img_size').value);
var img=document.querySelector('#img');
img.width=size;
img.height=size;
}
function doubleSize(){
console.log('Clikced double size!');
var img=document.querySelector('#img');
var size=parseInt(img.width)*2;
img.width=size;
img.height=size;
}
<img src='http://placehold.it/120x120&text=image1' width='200px' height="200px" id="img">
<input type="number" id='img_size' >
<button id="change_size" onclick="changeSize()"> Change Size</button>
<button id="double_size" onclick="doubleSize()">Double Size</button>

Cannot figure out why my script stops working. .

I'm making a simple program of an etch-a-sketch. I have 2 buttons. 1 that resets the screen, the other makes a new screen and lets you pick the number of pixels in the draw area. The default size works, and the reset works. When I click the new button and set the number of pixels in the draw area updates but the eventlistener stops working and the mouse over won't change the the background color of the divs anymore. Here is my code:
const screen = document.querySelector('.screen')
const clearButton = document.querySelector('.clear-btn');
const newButton = document.querySelector('.new-btn');
var size = 64;
function createGrid(size) {
document.styleSheets[0].cssRules[3].style["grid-template-columns"] = "repeat(" + size + ", 1fr)"
document.styleSheets[0].cssRules[3].style["grid-template-rows"] = "repeat(" + size + ", 1fr)"
console.log('createGrid');
for (i = 0; i < size*size; i++) {
const grid = document.createElement('div');
grid.classList.add('grid');
grid.style.cssText = 'color: #cccccc; background: #cccccc; border: solid 1px lightgrey;';
screen.appendChild(grid);
}
}
function reset() {
for (i = 0; i < size*size; i++) {
grid[i].style.backgroundColor = "#cccccc";
}
}
function newSize(){
let userResponse = prompt("Please enter size of canvas: ", "");
size = parseInt(userResponse);
remove();
createGrid(size);
}
function remove(){
while (screen.firstChild) {
console.log(size);
screen.removeChild(screen.firstChild);
}
}
createGrid(size);
clearButton.addEventListener('click', reset);
newButton.addEventListener('click', newSize);
var grid = document.getElementsByClassName('grid');
Array.from(grid).forEach((tile) => {
tile.addEventListener('mouseover', (e) => {
e.target.style.background = '#0d0d0d';
});
})
You need to add your event listener when you are creating the grid. You remove your grid, which the event original event listener has been attached and the newly created one doesn't have anything attached to it:
function createGrid(size) {
document.styleSheets[0].cssRules[3].style["grid-template-columns"] = "repeat(" + size + ", 1fr)"
document.styleSheets[0].cssRules[3].style["grid-template-rows"] = "repeat(" + size + ", 1fr)"
console.log('createGrid');
for (i = 0; i < size*size; i++) {
const grid = document.createElement('div');
grid.classList.add('grid');
grid.style.cssText = 'color: #cccccc; background: #cccccc; border: solid 1px lightgrey;';
screen.appendChild(grid);
// Add listener to grid cell
grid.addEventListener('mouseover', (e) => {
e.target.style.background = '#0d0d0d';
})
}
}
You might also have a look at event delegation so that you don't have to add a listener per cell and can add it on the container.
The reason for this is that you now have new divs. Your listener was attached to the original HTMLNodes, and now you added new ones, they do not have any listeners attached. The solution would be to:
- clean up the old listeners (in remove function)
- attach the new listeners (in newSize function)
Move the last part (from var grid = document.getElementsByClassName...) to a function, and call it at the end of new size function, something like this:
function atachListeners() {
const grid = document.getElementsByClassName('grid');
Array.from(grid).forEach(tile => {
tile.addEventListener('mouseover', ...);
});
};
Now, your newSize function is like this:
function newSize(){
let userResponse = prompt("Please enter size of canvas: ", "");
size = parseInt(userResponse);
remove();
createGrid(size);
attachListeners();
}
And the remove gets the addition:
function remove() {
removeTileListeners();
while (screen.firstChild) {
screen.removeChild(screen.firstChild);
}
}
Implementing the removeListeners() method, I leave to you as homework :)

how to detect an element inside a canvas?

I am trying to detect the drawn elements inside the canvas (for example: every slice in the pie) to put a click event on them later, but it seems not to be an easy task, I think the math would be the best way to carry it out, but where should I start.
Please Help me to find a solution for this.
here is the demo:
outer link
code is:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#myCanvas{
width: 50%;
height: 50%;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
class Piechart{
constructor(){
this.options = arguments[0];
this.canvas = arguments[0].canvas;
this.ctx = this.canvas.getContext('2d');
if(this.options.colors){
this.colors = arguments[0].colors;
}
else if (!this.options.colors){
this.colors = [
"#fde23e",
"#f16e23",
"#57d9ff",
"#937e88",
'#2fe678',
'#228888',
'#b111c1'
];
}
this.donut_hole = 0.5;
}
drawPie(){
var total_value = 0;
var color_index = 0;
for(var categ in this.options.data){
var val = this.options.data[categ];
total_value += val;
}
var start_angle = 0;
for(var categ in this.options.data){
var val = this.options.data[categ];
var slice_angle = 2*Math.PI*val/total_value;
this.drawPieSlice(
this.ctx,
this.canvas.width/2,
this.canvas.height/2,
Math.min(this.canvas.width/2,this.canvas.height/2),
start_angle,
start_angle+slice_angle,
this.colors[color_index%this.colors.length]
);
start_angle += slice_angle;
color_index++;
}
}
drawPieSlice(){
arguments[0].fillStyle = arguments[6];
arguments[0].beginPath();
arguments[0].moveTo(arguments[1],arguments[2]);
arguments[0].arc(
arguments[1],
arguments[2],
arguments[3],
arguments[4],
arguments[5]
);
arguments[0].closePath();
arguments[0].fill();
}
}
var myCanvas = document.getElementById("myCanvas");
var myCanvas_width = myCanvas.scrollWidth;
var myCanvas_height = myCanvas.scrollHeight;
myCanvas.width = myCanvas_width;
myCanvas.height = myCanvas_height;
var myVinyls = {
'Classical music':10,
'Rock':14,
'Pop':15,
'Jazz':4,
'test':6,
'test_1':7,
'test_2':8
};
var myPiechart = new Piechart(
{
canvas:myCanvas,
data:myVinyls,
colors:[
"#fde23e",
"#f16e23",
"#57d9ff",
"#937e88",
'#2fe678',
'#228888',
'#b111c1'
]
}
);
myPiechart.drawPie();
How to be able to detect each slice? Thank you.
EDIT
I have found an amazing solution for the purpose of adding hit region in the canvas and here is the link: Anton Lavrenov
First of all there are no elements on canvas you should understand this. It is a 2d plane where things are just drawn and if you want to move things around you need to redraw them entirely.
This said lets go back to the original problem.
Since you create the "object" in the canvas you know the size and distances of the object which you should keep in some js object for later use.
what i would do in this case to have an object that will hold the data of all other objects so when you click on the canvas you can get the click x and y and determine whether they are greater or equal to any of your objects' xs and ys
an example algorithm would be
if click.X>elem.X && click.X<elem.X+elem.Width
{
x is in check the same for y
}
if x__in&&y__in
{
you clicked the element
}

Javascript full screen display issues

I have been trying to put a text particle effect on my website. I have tested it on Codepen and works great but does not work on my website.
The text shows but the effect does not work as required. The text stays in a static position on the screen but the hover effect is below the word.
taking the mose through the text does nothing, if I move the mouse to the bottom of the screen the effect works on the text above.
The text stays static, however, when I move scroll down, the position of the text effect stays at the bottom of the screen until it ends up below the div and stops working all together.
I was wondering if someone could help me centre this behind the text.
Also I am keen to change the colour of the text, to a linear gradient from #125eaa to #d52027
The link to the page on my site - https://www.supplementgenie.co.uk/testpage
link to my codepen - https://codepen.io/Paulmcf87/pen/gjpgOB
However, when I run this code on JS Fiddle it gives me a 404 error?
You will see, running code below it works great as on Codepen. It doesnt seem to translate properly to a full screen
Any help would be great
The code I am using
var pixels=new Array();
var canv=$('canv');
var ctx=canv.getContext('2d');
var wordCanv=$('wordCanv');
var wordCtx=wordCanv.getContext('2d');
var mx=-1;
var my=-1;
var words="";
var txt=new Array();
var cw=0;
var ch=0;
var resolution=1;
var n=0;
var timerRunning=false;
var resHalfFloor=0;
var resHalfCeil=0;
function canv_mousemove(evt)
{
mx=evt.clientX-canv.offsetLeft;
my=evt.clientY-canv.offsetTop;
}
function Pixel(homeX,homeY)
{
this.homeX=homeX;
this.homeY=homeY;
this.x=Math.random()*cw;
this.y=Math.random()*ch;
//tmp
this.xVelocity=Math.random()*10-5;
this.yVelocity=Math.random()*10-5;
}
Pixel.prototype.move=function()
{
var homeDX=this.homeX-this.x;
var homeDY=this.homeY-this.y;
var homeDistance=Math.sqrt(Math.pow(homeDX,2) + Math.pow(homeDY,2));
var homeForce=homeDistance*0.01;
var homeAngle=Math.atan2(homeDY,homeDX);
var cursorForce=0;
var cursorAngle=0;
if(mx >= 0)
{
var cursorDX=this.x-mx;
var cursorDY=this.y-my;
var cursorDistanceSquared=Math.pow(cursorDX,2) + Math.pow(cursorDY,2);
cursorForce=Math.min(10000/cursorDistanceSquared,10000);
cursorAngle=Math.atan2(cursorDY,cursorDX);
}
else
{
cursorForce=0;
cursorAngle=0;
}
this.xVelocity+=homeForce*Math.cos(homeAngle) + cursorForce*Math.cos(cursorAngle);
this.yVelocity+=homeForce*Math.sin(homeAngle) + cursorForce*Math.sin(cursorAngle);
this.xVelocity*=0.92;
this.yVelocity*=0.92;
this.x+=this.xVelocity;
this.y+=this.yVelocity;
}
function $(id)
{
return document.getElementById(id);
}
function timer()
{
if(!timerRunning)
{
timerRunning=true;
setTimeout(timer,33);
for(var i=0;i<pixels.length;i++)
{
pixels[i].move();
}
drawPixels();
wordsTxt.focus();
n++;
if(n%10==0 && (cw!=document.body.clientWidth || ch!=document.body.clientHeight)) body_resize();
timerRunning=false;
}
else
{
setTimeout(timer,10);
}
}
function getRandomColor(min, max) {
return Math.random() * (max - min) + min;
}
function drawPixels()
{
var imageData=ctx.createImageData(cw,ch);
var actualData=imageData.data;
var index;
var goodX;
var goodY;
var realX;
var realY;
for(var i=0;i<pixels.length;i++)
{
goodX=Math.floor(pixels[i].x);
goodY=Math.floor(pixels[i].y);
for(realX=goodX-resHalfFloor; realX<=goodX+resHalfCeil && realX>=0 && realX<cw;realX++)
{
for(realY=goodY-resHalfFloor; realY<=goodY+resHalfCeil && realY>=0 && realY<ch;realY++)
{
index=(realY*imageData.width + realX)*4;
actualData[index+3]=realX;
actualData[index+2]=realX;
actualData[index+1]=realY;
}
}
}
imageData.data=actualData;
ctx.putImageData(imageData,0,0);
}
function readWords()
{
words=$('wordsTxt').value;
txt=words.split('\n');
}
function init()
{
readWords();
var fontSize=200;
var wordWidth=0;
do
{
wordWidth=0;
fontSize-=5;
wordCtx.font=fontSize+"px Avenir, sans-serif";
for(var i=0;i<txt.length;i++)
{
var w=wordCtx.measureText(txt[i]).width;
if(w>wordWidth) wordWidth=w;
}
} while(wordWidth>cw-50 || fontSize*txt.length > ch-50)
wordCtx.clearRect(0,0,cw,ch);
wordCtx.textAlign="center";
wordCtx.textBaseline="middle";
for(var i=0;i<txt.length;i++)
{
wordCtx.fillText(txt[i],cw/2,ch/2 - fontSize*(txt.length/2-(i+0.5)));
}
var index=0;
var imageData=wordCtx.getImageData(0,0,cw,ch);
for(var x=0;x<imageData.width;x+=resolution) //var i=0;i<imageData.data.length;i+=4)
{
for(var y=0;y<imageData.height;y+=resolution)
{
i=(y*imageData.width + x)*4;
if(imageData.data[i+3]>128)
{
if(index >= pixels.length)
{
pixels[index]=new Pixel(x,y);
}
else
{
pixels[index].homeX=x;
pixels[index].homeY=y;
}
index++;
}
}
}
pixels.splice(index,pixels.length-index);
}
function body_resize()
{
cw=document.body.clientWidth;
ch=document.body.clientHeight;
canv.width=cw;
canv.height=ch;
wordCanv.width=cw;
wordCanv.height=ch;
init();
}
wordsTxt.focus();
wordsTxt.value="Supplement Genie";
resolution=1;
resHalfFloor=Math.floor(resolution/2);
resHalfCeil=Math.ceil(resolution/2);
body_resize();
timer();
#wordsTxt{
display:none
}
div.pixeltext canvas{
width:98vw;
height:100vh;
}
div.pixeltext{
background-color: #d52027;
<div class="pixeltext">
<canvas id="canv" onmousemove="canv_mousemove(event);" onmouseout="mx=-1;my=-1;">
you need a canvas-enabled browser, such as Google Chrome
</canvas>
<canvas id="wordCanv" width="500px" height="500px" style="border:1px solid black;display:none;">
</canvas>
<textarea id="wordsTxt" style="position:absolute;left:-100;top:-100;" onblur="init();" onkeyup="init();" onclick="init();"></textarea>
<div>
You should define the cw,ch variables based on the canvas dimensions and not on the document.body.clientWidth etc.
Also you should avoid using canv.offsetLeft and canv.offsetTop since your element is inside other positioned elements and the offset properties return the position relative to the closest positioned ancestor.
Using canv.getBoundingClientRect which returns the width/height and top/left/right/bottom properties relative to the viewport will solve the problem.
(your codepen was also using wrong names of the methods you had created)
See https://codepen.io/gpetrioli/pen/rreagp which basically modifies the following methods
function canvMousemove(evt) {
var rect = canv.getBoundingClientRect();
mx = evt.clientX - rect.left;
my = evt.clientY - rect.top;
}
function body_resize() {
var rect = canv.getBoundingClientRect()
cw = rect.width;
ch = rect.height;
console.log(ch);
canv.width = cw;
canv.height = ch;
wordCanv.width = cw;
wordCanv.height = ch;
init();
}
In your site i noticed a timer method which should also be modified to
function timer() {
var rect;
if (!timerRunning) {
timerRunning = true;
setTimeout(timer, 33);
for (var i = 0; i < pixels.length; i++) {
pixels[i].move();
}
drawPixels();
wordsTxt.focus();
n++;
rect = canv.getBoundingClientRect();
if (n % 10 == 0 && (cw != rect.width || ch != rect.height)) body_resize();
timerRunning = false;
} else {
setTimeout(timer, 10);
}
}

In Javascript how can I set rgba without specifying the rgb?

I have an HTML element whose background colour is set with rgba()
<div style="background-color: rgba(2,100,100,0);"> </div>
Then I have a timer that makes the background slowly fade in by changing the opacity value of the element in javascript
myEle.style.backgroundColor = "rgba(x,x,x,0.1)"; // I have to know the rgb values to update the alpha value
Is there a way to set the a value of rgba() without changing/knowing the rgb values?
Maybe I can do something like this?
var r = myEle.style.r;
var g = myEle.style.g;
var b = myEle.style.b;
myEle.style.backgroundColor = "rgba("+r+","+g+","+b+",0.1)";
You got the string, replace whatever
var oldCss = 'rgba(1,1,1,0.3)',
newOpacity = '0.5',
newCss = oldCss.replace(/[^,]+(?=\))/, newOpacity);
console.log(oldCss, "replaced with", newCss);
After some playing around, and the discovery of getComputedStyle, I have put together this.
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
#element {
background-color: rgb(10,10,10);
background-color: rgba(10,10,10,1);
}
</style>
<script type="text/javascript">
HTMLElement.prototype.alpha = function(a) {
current_color = getComputedStyle(this).getPropertyValue("background-color");
match = /rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*\d+[\.\d+]*)*\)/g.exec(current_color)
a = a > 1 ? (a / 100) : a;
this.style.backgroundColor = "rgba(" + [match[1],match[2],match[3],a].join(',') +")";
}
</script>
</head>
<body>
<div id="element">
This is some content.
</div>
<script type="text/javascript">
e = document.getElementById('element');
e.alpha(20);
</script>
</body>
</html>
Make sure you define in your css your values, and cascade because RGBA is CSS3.
Also see that you can pass in a number >1 for alpha and it will divide by 100 for you (I hate working with decimals when thinking percentages).
Enjoy!
I had do this too but ended up writing something a little more specific. I put it in a jQuery plugin that accepts a min and max opacity:
$.fn.setAlpha = function ( options ) {
var settings = $.extend({
alpha: 0.5,
min: 0,
max: 1
}, options );
return this.each(function() {
var color = $(this).css('background-color');
if (color.substring(0,4) === 'rgba') {
var a;
if (settings.alpha <= settings.min) {
a = settings.min;
} else if (settings.alpha >= settings.max) {
a = settings.max;
} else {
a = settings.alpha;
}
var rgba = color.replace(/[^,]+(?=\))/, a);
$(this).css('background-color', rgba);
}
});
}
$.fn.getAlpha = function () {
var color = this.css('background-color');
if (color.substring(0,4) === 'rgba') {
var alpha = color.split(',');
alpha = alpha[alpha.length - 1].trim();
alpha = alpha.substring(0, alpha.indexOf(")"));
return alpha;
} else {
return 1;
}
}
then you use them to do something like this to set a div to transparent and fade it to its original opacity as you scroll down:
//get original opacity
var originalOpacity = $('#myDiv').getAlpha();
//set new opacity
//it will be 0 at the top of the page
var newOpacity = $(window).scrollTop()/500;
$('#myDiv').setAlpha({"alpha": newOpacity, "max": originalOpacity});
//on scroll fade new opacity to originalOpacity at 500px down
$(window).scroll( function() {
var newOpacity = $(window).scrollTop()/500;
$('#myDiv').setAlpha({"alpha": newOpacity, "max": originalOpacity});
}
Does this help?
http://www.phpied.com/rgb-color-parser-in-javascript/
This may help in addition.
Convert RGBA color to RGB
You could also use elem.style.opacity=0.5 or in html style="opacity:0.5". It is important to note that the child nodes will fade too.

Categories

Resources