How to make matrix like text change effect in javascript? - javascript

I was trying to make this text change matrix movie like effect in JavaScript.The basic concept was that there is a div present in html and JavaScript take that div's inner text and manipulate it to create the continuously changing random text effect like in matrix movie. I am quite new to JavaScript, I am having hard time figuring out the logic behind the animation like the each steps, one step after another like what will happen next in whole animation process.
Anyways, I tried to make it on my own but as you can suspect i failed.
Here is my code :
<html>
<head>
<script>
var text = document.getElementById("text").innerText;
var length_var = text.length;
var possible = [];
var possible_text ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var counter = 0;
var arr_text = [];
var new_func = function(){
arr_text[Math.floor(Math.random() * length_var)] = possible.charAt(Math.floor(Math.random() * possible.length));
alert(arr_text);
};
var call_func = function(){
for(var i=0; i<=possible_text.length; i++){
possible[i] = possible_text.charAt(i);
}
for(var i=0; i<= length_var ; i++){
arr_text[i] = text.charAt(i);
}
setInterval(new_func, 100);
};
</script>
</head>
<body onload="call_func()">
<div id="text">
Hello There!
</div>
</body>
</html>
What I was planning to do can be seen on this page, as I was highly motivated to do this effect on my own.
Link : http://events.ccc.de/congress/2012/wiki/Main_Page
The header text of the page contains such animation.
Please Help

This changes the string sequentially.
function main() {
"use strict";
var counter = 0, all = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var $_inter = setInterval(function() {
var text = document.getElementById("text");
text.innerHTML = text.innerHTML.substring(0, counter) + all.charAt(Math.floor(Math.random()*all.length)) + text.innerHTML.substring(counter+1);
counter = (counter+1)%text.innerHTML.length;
}, 100);
}
window.onload = main;

Try this-
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function randString(len) {
"use strict";
var i, out="", all ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (i = 0; i < len; i++) {
out += all.charAt(Math.floor(Math.random()*all.length));
}
return out;
}
function main() {
"use strict";
var $_inter = setInterval(function() {
var text = document.getElementById("text");
text.innerHTML = randString(text.innerHTML.length);
}, 100);
}
window.onload = main;
</script>
</head>
<body>
<div id="text">Hello World!</div>
</body>
</html>

Here take a look at this JSFiddle, as put together from this resource. It is very fast and you can easily configure it.
Basically creates a blank canvas and renders the graphics. Here is the JS and HTML code that does that:
HTML:
<html style="background:black; width:100%; height:100%">
<body style="background:black; width:100%; height:100%">
<canvas id="c" style="display: block;"></canvas>
</body>
</html>
JS:
var c = document.getElementById("c");
var ctx = c.getContext("2d");
//making the canvas full screen
c.height = window.innerHeight;
c.width = window.innerWidth;
//chinese characters - taken from the unicode charset
var chinese = "田由甲申甴电甶男甸甹町画甼甽甾甿畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑";
//converting the string into an array of single characters
chinese = chinese.split("");
var font_size = 10;
var columns = c.width/font_size; //number of columns for the rain
//an array of drops - one per column
var drops = [];
//x below is the x coordinate
//1 = y co-ordinate of the drop(same for every drop initially)
for(var x = 0; x < columns; x++)
drops[x] = 1;
//drawing the characters
function draw()
{
//Black BG for the canvas
//translucent BG to show trail
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#0F0"; //green text
ctx.font = font_size + "px arial";
//looping over drops
for(var i = 0; i < drops.length; i++)
{
//a random chinese character to print
var text = chinese[Math.floor(Math.random()*chinese.length)];
//x = i*font_size, y = value of drops[i]*font_size
ctx.fillText(text, i*font_size, drops[i]*font_size);
//sending the drop back to the top randomly after it has crossed the screen
//adding a randomness to the reset to make the drops scattered on the Y axis
if(drops[i]*font_size > c.height && Math.random() > 0.975)
drops[i] = 0;
//incrementing Y coordinate
drops[i]++;
}
}
setInterval(draw, 33);

The crux of the program that's on the site (which is really, really ugly) involves creating an array of "haXX0r-like" characters, and then injecting them and removing them from the text.
They also speed up and slow down their process, and bounce between doing an addition pass and a removal pass, from what I saw on my quick read-through.
The downside of their code are that it's all a bunch of loops slopped together, with a bunch of "if"s to contain two or three loops, one after the other... ...and then they add "mode-switching" to that, where they say "if we're in mode-1 add stuff and do it quickly, and if we're in mode-2, remove stuff and lower the speed and if we're in this submode of either mode, set the speed accordingly, and if the speed is greater than this or less than that, and we're in this mode, then stop what we're doing, wait 5 seconds and call it again"...
...not pretty.
But they're starting with a quote, finding a random spot in the string, and then replacing the character at that spot with the character, plus the new "<", "?", "{", etc...
And speeding up and slowing down, as they add and remove the randomly-chosen character-type.

Elegant snippet
canvas = document.body.appendChild(document.createElement('canvas'))
screen = window.screen;
width = canvas.width = screen.width;
height = canvas.height = screen.height;
p = Array(255).join(1).split('');
context = canvas.getContext('2d');
setInterval(function(){
context.fillStyle = 'rgba(0,0,0,0.05)';
context.fillRect(0, 0, width, height);
context.fillStyle = 'rgba(0,255,0,1)';
p = p.map(function(v,i){
r = Math.random();
context.fillText(String.fromCharCode(Math.floor(2720 + r * 33)),i*10,v);
v += 10;
return v > 768 + r * 1e4 ? 0 : v
})
}, 33)

If you want something that will randomise a string and slowly replace each character with the original, here's something that may suit. The replacement order is random too, so the string is replaced out of order but ends up with the original string.
A fancier solution is to give each letter its own timer with a different lag so they run at different speeds, and for different lengths of time. But that might suck some serious system resources for a big string.
function Randomiser(el, count, delay) {
this.element = el;
this.originalText = el.textContent || el.innerText || '';
this.places = [];
this.currentText = [];
this.count = count || 3; // iterations before fixing a character
this.delay = delay || 100; // milliseconds between updates
this.iteration = 0;
this.startTime = new Date();
var i = this.originalText.length;
while (i--) {
this.places[i] = [i];
}
}
Randomiser.prototype.randomise = function() {
var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var i = this.places.length;
while (i--) {
this.currentText[this.places[i]] = charSet.charAt((Math.random() * charSet.length) | 0);
}
this.iteration += 1;
}
Randomiser.prototype.setContent = function() {
var t = this.currentText.join('');
if (typeof this.element.textContent == 'string') {
this.element.textContent = t;
} else {
this.element.innerText = t;
}
}
Randomiser.prototype.run = function() {
var n;
var temp = [];
// If first run, randomise to initiate
if (!this.iteration) {
this.randomise();
}
// If there are places left
if (this.places.length) {
// If reached count, randomly remove one place and set its character
// to the original value
if (!(this.iteration % this.count)) {
n = this.places.splice((Math.random() * this.places.length|0), 1)[0];
this.currentText[n] = this.originalText.charAt(n);
}
// Randomise the string and call itself
this.randomise();
this.setContent();
var randomiser = this;
setTimeout(function(){randomiser.run();}, this.delay);
}
// If no places left, end
}
// Kick it off
var r = new Randomiser(document.getElementById('text'), 5, 200);
r.run();
The above uses classic prototype inheritance, it could use a closure for the same thing. Also all those prototype functions could be put in a single object that is assigned to Randomiser.prototype.

Related

How to wrap text in Node canvas? [duplicate]

I am trying to add text on an image using the <canvas> element. First the image is drawn and on the image the text is drawn. So far so good.
But where I am facing a problem is that if the text is too long, it gets cut off in the start and end by the canvas. I don't plan to resize the canvas, but I was wondering how to wrap the long text into multiple lines so that all of it gets displayed. Can anyone point me at the right direction?
Updated version of #mizar's answer, with one severe and one minor bug fixed.
function getLines(ctx, text, maxWidth) {
var words = text.split(" ");
var lines = [];
var currentLine = words[0];
for (var i = 1; i < words.length; i++) {
var word = words[i];
var width = ctx.measureText(currentLine + " " + word).width;
if (width < maxWidth) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
We've been using this code for some time, but today we were trying to figure out why some text wasn't drawing, and we found a bug!
It turns out that if you give a single word (without any spaces) to the getLines() function, it will return an empty array, rather than an array with a single line.
While we were investigating that, we found another (much more subtle) bug, where lines can end up slightly longer than they should be, since the original code didn't account for spaces when measuring the length of a line.
Our updated version, which works for everything we've thrown at it, is above. Let me know if you find any bugs!
A possible method (not completely tested, but as for now it worked perfectly)
/**
* Divide an entire phrase in an array of phrases, all with the max pixel length given.
* The words are initially separated by the space char.
* #param phrase
* #param length
* #return
*/
function getLines(ctx,phrase,maxPxLength,textStyle) {
var wa=phrase.split(" "),
phraseArray=[],
lastPhrase=wa[0],
measure=0,
splitChar=" ";
if (wa.length <= 1) {
return wa
}
ctx.font = textStyle;
for (var i=1;i<wa.length;i++) {
var w=wa[i];
measure=ctx.measureText(lastPhrase+splitChar+w).width;
if (measure<maxPxLength) {
lastPhrase+=(splitChar+w);
} else {
phraseArray.push(lastPhrase);
lastPhrase=w;
}
if (i===wa.length-1) {
phraseArray.push(lastPhrase);
break;
}
}
return phraseArray;
}
Here was my spin on it... I read #mizar's answer and made some alterations to it... and with a little assistance I Was able to get this.
code removed, see fiddle.
Here is example usage. http://jsfiddle.net/9PvMU/1/ - this script can also be seen here and ended up being what I used in the end... this function assumes ctx is available in the parent scope... if not you can always pass it in.
edit
the post was old and had my version of the function that I was still tinkering with. This version seems to have met my needs thus far and I hope it can help anyone else.
edit
It was brought to my attention there was a small bug in this code. It took me some time to get around to fixing it but here it is updated. I have tested it myself and it seems to work as expected now.
function fragmentText(text, maxWidth) {
var words = text.split(' '),
lines = [],
line = "";
if (ctx.measureText(text).width < maxWidth) {
return [text];
}
while (words.length > 0) {
var split = false;
while (ctx.measureText(words[0]).width >= maxWidth) {
var tmp = words[0];
words[0] = tmp.slice(0, -1);
if (!split) {
split = true;
words.splice(1, 0, tmp.slice(-1));
} else {
words[1] = tmp.slice(-1) + words[1];
}
}
if (ctx.measureText(line + words[0]).width < maxWidth) {
line += words.shift() + " ";
} else {
lines.push(line);
line = "";
}
if (words.length === 0) {
lines.push(line);
}
}
return lines;
}
context.measureText(text).width is what you're looking for...
Try this script to wrap the text on a canvas.
<script>
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '';
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
ctx.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
}
else {
line = testLine;
}
}
ctx.fillText(line, x, y);
}
var canvas = document.getElementById('Canvas01');
var ctx = canvas.getContext('2d');
var maxWidth = 400;
var lineHeight = 24;
var x = (canvas.width - maxWidth) / 2;
var y = 70;
var text = 'HTML is the language for describing the structure of Web pages. HTML stands for HyperText Markup Language. Web pages consist of markup tags and plain text. HTML is written in the form of HTML elements consisting of tags enclosed in angle brackets (like <html>). HTML tags most commonly come in pairs like <h1> and </h1>, although some tags represent empty elements and so are unpaired, for example <img>..';
ctx.font = '15pt Calibri';
ctx.fillStyle = '#555555';
wrapText(ctx, text, x, y, maxWidth, lineHeight);
</script>
</body>
See demo here http://codetutorial.com/examples-canvas/canvas-examples-text-wrap.
From the script here: http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/
I've extended to include paragraph support. Use \n for new line.
function wrapText(context, text, x, y, line_width, line_height)
{
var line = '';
var paragraphs = text.split('\n');
for (var i = 0; i < paragraphs.length; i++)
{
var words = paragraphs[i].split(' ');
for (var n = 0; n < words.length; n++)
{
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > line_width && n > 0)
{
context.fillText(line, x, y);
line = words[n] + ' ';
y += line_height;
}
else
{
line = testLine;
}
}
context.fillText(line, x, y);
y += line_height;
line = '';
}
}
Text can be formatted like so:
var text =
[
"Paragraph 1.",
"\n\n",
"Paragraph 2."
].join("");
Use:
wrapText(context, text, x, y, line_width, line_height);
in place of
context.fillText(text, x, y);
I am posting my own version used here since answers here weren't sufficient for me. The first word needed to be measured in my case, to be able to deny too long words from small canvas areas. And I needed support for 'break+space, 'space+break' or double-break/paragraph-break combos.
wrapLines: function(ctx, text, maxWidth) {
var lines = [],
words = text.replace(/\n\n/g,' ` ').replace(/(\n\s|\s\n)/g,'\r')
.replace(/\s\s/g,' ').replace('`',' ').replace(/(\r|\n)/g,' '+' ').split(' '),
space = ctx.measureText(' ').width,
width = 0,
line = '',
word = '',
len = words.length,
w = 0,
i;
for (i = 0; i < len; i++) {
word = words[i];
w = word ? ctx.measureText(word).width : 0;
if (w) {
width = width + space + w;
}
if (w > maxWidth) {
return [];
} else if (w && width < maxWidth) {
line += (i ? ' ' : '') + word;
} else {
!i || lines.push(line !== '' ? line.trim() : '');
line = word;
width = w;
}
}
if (len !== i || line !== '') {
lines.push(line);
}
return lines;
}
It supports any variants of lines breaks, or paragraph breaks, removes double spaces, as well as leading or trailing paragraph breaks. It returns either an empty array if the text doesn't fit. Or an array of lines ready to draw.
look at https://developer.mozilla.org/en/Drawing_text_using_a_canvas#measureText%28%29
If you can see the selected text, and see its wider than your canvas, you can remove words, until the text is short enough. With the removed words, you can start at the second line and do the same.
Of course, this will not be very efficient, so you can improve it by not removing one word, but multiple words if you see the text is much wider than the canvas width.
I did not research, but maybe their are even javascript libraries that do this for you
I modified it using the code from here http://miteshmaheta.blogspot.sg/2012/07/html5-wrap-text-in-canvas.html
http://jsfiddle.net/wizztjh/kDy2U/41/
This should bring the lines correctly from the textbox:-
function fragmentText(text, maxWidth) {
var lines = text.split("\n");
var fittingLines = [];
for (var i = 0; i < lines.length; i++) {
if (canvasContext.measureText(lines[i]).width <= maxWidth) {
fittingLines.push(lines[i]);
}
else {
var tmp = lines[i];
while (canvasContext.measureText(tmp).width > maxWidth) {
tmp = tmp.slice(0, tmp.length - 1);
}
if (tmp.length >= 1) {
var regex = new RegExp(".{1," + tmp.length + "}", "g");
var thisLineSplitted = lines[i].match(regex);
for (var j = 0; j < thisLineSplitted.length; j++) {
fittingLines.push(thisLineSplitted[j]);
}
}
}
}
return fittingLines;
And then get draw the fetched lines on the canvas :-
var lines = fragmentText(textBoxText, (rect.w - 10)); //rect.w = canvas width, rect.h = canvas height
for (var showLines = 0; showLines < lines.length; showLines++) { // do not show lines that go beyond the height
if ((showLines * resultFont.height) >= (rect.h - 10)) { // of the canvas
break;
}
}
for (var i = 1; i <= showLines; i++) {
canvasContext.fillText(lines[i-1], rect.clientX +5 , rect.clientY + 10 + (i * (resultFont.height))); // resultfont = get the font height using some sort of calculation
}
This is a typescript version of #JBelfort's answer.
(By the way, thanks for this brilliant code)
As he mentioned in his answer this code can simulate html element such as textarea,and also the CSS property
word-break: break-all
I added canvas location parameters (x, y and lineHeight)
function wrapText(
ctx: CanvasRenderingContext2D,
text: string,
maxWidth: number,
x: number,
y: number,
lineHeight: number
) {
const xOffset = x;
let yOffset = y;
const lines = text.split('\n');
const fittingLines: [string, number, number][] = [];
for (let i = 0; i < lines.length; i++) {
if (ctx.measureText(lines[i]).width <= maxWidth) {
fittingLines.push([lines[i], xOffset, yOffset]);
yOffset += lineHeight;
} else {
let tmp = lines[i];
while (ctx.measureText(tmp).width > maxWidth) {
tmp = tmp.slice(0, tmp.length - 1);
}
if (tmp.length >= 1) {
const regex = new RegExp(`.{1,${tmp.length}}`, 'g');
const thisLineSplitted = lines[i].match(regex);
for (let j = 0; j < thisLineSplitted!.length; j++) {
fittingLines.push([thisLineSplitted![j], xOffset, yOffset]);
yOffset += lineHeight;
}
}
}
}
return fittingLines;
}
and you can just use this like
const wrappedText = wrapText(ctx, dialog, 200, 100, 200, 50);
wrappedText.forEach(function (text) {
ctx.fillText(...text);
});
}

updatePixels() not actually updating the pixels

I'm trying to make a program that takes a string, converts it to base64, and then to binary. It then takes the binary and changes the pixels a black pixel for 0 and a white pixel for 1.
I've gotten the pixel array to change to what I want, but it's not actually changing when I call updatePixels().
My goal is to then take the canvas and export it as an image.
My sketch:
let hw;
let input, button;
let binaryOut;
function setup() {
createCanvas(140,140);
input=createInput();
pixelDensity(1);
button = createButton("get image");
button.mousePressed(txtTo64ToBin)
loadPixels();
}
function txtTo64ToBin(){
str = input.value();
str = btoa(str);
let output = '';
str = str.split("")
for(let i=0;i<str.length;i++){
let base = str[i].charCodeAt(0).toString(2)
while(base.length < 8){
base = "0"+base;
}
output += base;
}
binaryOut = output;
console.log(binaryOut)
updateImage(binaryOut.split(''))
}
function updateImage(binArr){
hw = factors(binArr.length);
hw = hw[hw.length-1];
console.log(hw);
resizeCanvas(...hw,false)
pixels = []
for(let i=0; i<binArr.length; i++){
pixels[i*4] = map(binArr[i],0,1,0,255);
pixels[i*4+1] = map(binArr[i],0,1,0,255);
pixels[i*4+2] = map(binArr[i],0,1,0,255);
pixels[i*4+3] = 255;
}
console.log(pixels)
updatePixels() //here is the updatePixels function call
}
function draw() {
noLoop();
}
function factors(num) {
var half = Math.floor(num / 2),
arr = [],
i, j;
num % 2 === 0 ? (i = 2, j = 1) : (i = 3, j = 2);
for (i; i <= half; i += j) {
if(num % i === 0 && i <= num/i){
arr.push([i,num/i]);
}
}
return arr;
}
I'm very confused and any help would be much appreciated.
Please try to break your problem down into smaller steps and isolate the problem in a smaller example.
Here is an example sketch that shows the same problem:
let button;
function setup() {
createCanvas(140,140);
button = createButton("test");
button.mousePressed(updateImage);
loadPixels();
}
function updateImage(){
pixels = [];
for(let i=0; i < width * height; i++){
pixels[i*4] = 255;
pixels[i*4+1] = 0;
pixels[i*4+2] = 0;
pixels[i*4+3] = 255;
}
updatePixels();
}
function draw() {
noLoop();
}
We might expect this to turn the canvas red when we click the button, but it does not. See how this example is easier to play with, because we don't have to think about any of your logic?
Anyway, the problem is caused by this line:
pixels = [];
Take that line out, and the example program works.
My guess is this is because pixels is not a standard JavaScript array. From the reference:
Uint8ClampedArray containing the values for all the pixels in the display window.
...
Note that this is not a standard javascript array.

PaperJs Add 2 raster as 2 symbols in the same project

I have this project in paperjs:
var url = "http://www.clker.com/cliparts/q/I/s/P/E/3/yellow-umbrella-md.png";
raster = new Raster(url);
raster.rotate(10);
raster.scale(0.4);
var url2 = "https://images.vexels.com/media/users/3/145373/isolated/preview/98721f602aa3fadb040e0a161ab3f966-waterdrop-vislumbrante-vis-o-ilustra--o-by-vexels.png";
secondRaster = new Raster(url);
secondRaster.scale(0.9);
var count = 150;
var symbol = new Symbol(raster);
var secondSymbol = new Symbol(secondRaster);
for (var i = 0; i < count; i++) {
// The center position is a random point in the view:
var center = Point.random() * view.size;
var placedSymbol = symbol.place(center);
placedSymbol.scale(i / count);
}
function onFrame(event) {
// Run through the active layer's children list and change
// the position of the placed symbols:
for (var i = 0; i < count; i++) {
var item = project.activeLayer.children[i];
// Move the item 1/20th of its width to the right. This way
// larger circles move faster than smaller circles:
item.position.y += item.bounds.width / 80;
// If the item has left the view on the right, move it back
// to the left:
if (item.bounds.bottom > view.size.width) {
item.position.y = -item.bounds.width;
}
}
}
The first raster has a symbol works good, but the second can't make it work... I read about to add more than one symbol to project.activeLayer.children but don't work. Even if I do a group of an array with both symbols also don't show up.
I read in a post that symbols can't be added as a group. Being that be true, it should be ok to be added even though isolated...
Anybody had done something similar?
Thank you
There are some mistakes in your code:
The most important one, that make you think that the second raster doesn't work, is that you are creating the second raster with the variable url instead of url2. So both rasters use the same image as source...
You need to place the second symbol like you do with the first one otherwise it will never get rendered.
When iterating through active layer children, make sure to iterate over all children by using project.activeLayer.children.length (as you are placing count * 2 symbols).
When checking for bottom reaching items, use height instead of width.
Here is a sketch demonstrating the solution.
var COUNT = 10;
var raster = new Raster('http://www.clker.com/cliparts/q/I/s/P/E/3/yellow-umbrella-md.png');
raster.rotate(10);
raster.scale(0.4);
var secondRaster = new Raster('https://images.vexels.com/media/users/3/145373/isolated/preview/98721f602aa3fadb040e0a161ab3f966-waterdrop-vislumbrante-vis-o-ilustra--o-by-vexels.png');
secondRaster.scale(0.15);
var symbol = new Symbol(raster);
var secondSymbol = new Symbol(secondRaster);
for (var i = 1; i <= COUNT; i++) {
// first symbol
symbol.place(Point.random() * view.size).scale(i / COUNT);
// second symbol
secondSymbol.place(Point.random() * view.size).scale(i / COUNT);
}
function onFrame(event) {
for (var i = 0; i < project.activeLayer.children.length; i++) {
var item = project.activeLayer.children[i];
item.position.y += item.bounds.height / 80;
if (item.bounds.bottom > view.size.height) {
item.position.y = -item.bounds.height;
}
}
}

Unable to set object property in storing to an array of objects (JavaScript)

I'm trying to create an array of objects for a simple canvas-based Space Invaders game.
I have created a space invader object, and an array of space invaders. I want to slightly change the horizontal position and other properties of each invader before it is added to the array. This is how I'm trying to do it:
// Invaders
invaders = new Array();
invader = {
x: 0,
y: 0,
size: 25,
xspeed: 0.25,
yspeed: 0.1,
alive: 1,
letter: ""
};
invadersMovingLeft = true;
kills = 0;
word = "interesting";
numberOfInvaders = word.length;
letters = word.split('');
letterNumber = 0;
xpos = 0;
invaderSpacing = 50;
exploding = false;
shuffledLetters = shuffle(letters);
hitLetters = "";
for (i = 0; i < numberOfInvaders; i++) {
invader['letter'] = shuffledLetters[letterNumber];
invader['x'] = xpos;
xpos = xpos + invaderSpacing;
letterNumber = letterNumber + 1;
invaders[i] = invader;
console.log(invaders);
}
The console log shows that each invader has the exact same properties. Any idea what's going wrong here? I'm new at working with objects, and am probably making a beginner's mistake.
The problem is you are trying to use invader as a base object, which makes all of the invaders refer to the same object.
Instead of that, have an invader which acts like a class, that you can instantiate to make a new invader. Each invader is then a new independent instance, which you can push to the array.
function invader(){
this.x = 0;
this.y = 0;
this.size = 25;
this.xspeed = 0.25;
this.yspeed = 0.1;
this.alive = 1;
this.letter = "";
}
var invaders=new Array();
var inv;
for(i=0;i<numberOfInvaders;i++){
inv = new invader();
inv.letter = shuffledLetters[letterNumber];
inv.x = xpos;
xpos = xpos+invaderSpacing;
letterNumber = letterNumber+1;
invaders.push(inv);
}
console.log(invaders);
Try adding
var newInvader = new invader();
to your loop. (first line)
Then change the properties of newInvader to what you want, then add newInvader to the array instead of invader.

Raphael JS on rect click redirect to url takes the last url for all rectangles

I am trying to iterate over the list and create the rectangle boxes, and on click of each user should be redirected to specific page, though struggling to understand the click handler. Please help me to refine below code, so that it redirects to corresponding url instead of the last one. (I think its my lack of knowledge in JS itself.)
var items = [{'url': 'http://google.com'}, {'url': 'http://stackoverflow.com'}];
var bh = 120;
var bw = 120;
var br = 8;
var start_x = 100;
var start_y = 80;
r = Raphael("holder", 840, 780)
for (var i = 0; i < items.length; i++){
group = r.set()
group.push(r.rect(start_x, start_y, bh, bw, br));
start_x = start_x+200;
group[0].node.onclick = function(){
alert(items[i].url);
};
}
jsFiddle Demo
Above code is kind of version what I am working on and it renders multiple rects on SVG, the problem I am running into is on click of the rect, it returns the last one only.
Thanks.
The problem is that there's one variable called i, and after the loop is over its value is items.length. You need to remember the correct value for each node. Try this:
for (var i = 0; i < items.length; i++){
...
var rect = r.rect(start_x, start_y, bh, bw, br);
rect.node.setAttribute('data-index', i);
group.push(rect);
...
rect.node.onclick = function(event) {
alert(items[event.target.getAttribute('data-index')].url);
};
}

Categories

Resources