variable scope conflict Js - javascript

I am trying to make a simple animation web application using paperjs, I have been reading the docs for it but I can't really grasp how this code works.
If you can provide me with headlines to search for or exactly what are the js parts that I am missing.
// To make sure the script isn't executed unless DOM is ready
window.onload = function(){
// Whenever the user clicks on a keyboard button, this event handler is executed
$('body').on('keypress', function(event) {
var path = new paper.Path.Circle({
center: [400, 200],
radius: 10,
fillColor: '#1abc9c'
});
console.log(path.fillColor); // prints undefined
console.log(path.fillColor.hue); // prints undefined
paper.view.onFrame = function(event) {
// On each frame, increment:
path.fillColor.hue += 1; // works fine !!
path.radius -= 1; // doesn't work !!
}
// starts drawing
paper.view.draw();
}
}
First question: How is path.fillColor.hue is undefined in the first console.log() line but the first line inside paper.view.onframe() works fine ?
Second question: By the same concept as path.fillColor.hue I am trying to make another animation that shrinks the radius of my circles so I am decrementing path.radius in my animation function, however, this doen't work -though no error is raised.
Notes:
1- hue is a property that changes the color of my circle, I got it from the docs.
2- I tried to put paper.view.draw() inside my paper.view.onframe() function as a solution as if it will draw a new circle with the new radius every time the paper.view.onframe() is called but this doesn't work.
3- I know that my code contains lots of repeating paper. and the paperjs docs purposes 2 conventions to avoid this but I am just trying to keep my mind on the most important things first before improving my code quality.
4- This isn't the complete code and I know that using Jquery isn't really useful as I could have replaced its use with addEventListener() but keypress event seems to have a problem so I decided to get back to it when I finish the code and eliminate jquery from my project.

The issue is that the path is no longer a "circle" once you have created it. It's just a path. You need to interact with it in a slightly more complicated way to change the radius.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="myCanvas" resize="true" width="800" height="800"></canvas>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="paper-full.min.js"></script>
<script>
window.onload = function(){
$('body').on('keypress', function(event) {
paper.setup('myCanvas');
var path = new paper.Path.Circle({
center: [400, 200],
radius: 10,
fillColor: '#1abc9c'
});
paper.view.onFrame = function(event) {
var currentRadius = path.bounds.width / 2,
newRadius = currentRadius - 1;
path.fillColor.hue += 1;
path.scale(newRadius / currentRadius);
}
paper.view.draw();
});
}
</script>
</body>
</html>
I got the necessary info from https://groups.google.com/forum/#!topic/paperjs/xJTDWTQIFwQ which also discusses a slight problem with doing it this way that doesn't apply to your use case as presented.

Related

JavaScript - Modify function to be reusable in later instances?

This is a followup to this question.
I have a function that I would like to make reusable so I don't have to make new, very similar functions over and over to achieve the same effect. Specifically, I would like to know how to change var message after the first function instance runs without losing the original message.
Here's my code:
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
//Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
// Let's see if we can add some fade-in buttons, shall we?
//(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
var timer = setInterval(dialogue, 20);
function dialogue(add = 1){ // By default 1 character is made visible
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(message.substr(0, len)); // Make the change
if (len < message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on page load
$("#button1").hide();
<!DOCTYPE HTML>
<html>
<head>
<title>Sandbox</title>
<link rel="stylesheet" href="mainstyle.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p id="pid"></p>
<button id="button1">Ooh, here's one! Click to see what it does!</button>
</body>
</html>
edit: https://jsfiddle.net/n8Lczdk0/4/
I'm not sure what you mean, but if you wrap everything in a function that takes message as an argument, then it'll be in the dialogue function's closure and you'll be able to update var message after you call the wrapper function without dialogue() knowing about it. As they say, a few lines of code are worth hundreds of prose:
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
Let's see if we can add some fade-in buttons, shall we?
(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
const f = message => {
var timer = setInterval(dialogue, 20);
function dialogue(add = 1){ // By default 1 character is made visible
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(message.substr(0, len)); // Make the change
if (len < message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on function call
$("#button1").hide();
}
f(message)
message = "some new value"
So above, I'm essentially wrapping your whole js code in a function that takes message as an argument. Kinda like currying your dialogue function.
You could also pass your ids as arguments and make it fully reusable. Just pass a message and DOM ids to the function and the magic unrolls with associated buttons fading in as various texts show up.
Hope this helped, cheers,
thomas
You can try with below one way
You can pass one optional argument(message) to the function and check if it is passed then use that message otherwise use default original message.
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
Let's see if we can add some fade-in buttons, shall we?
(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
var timer = setInterval(dialogue, 20);
function dialogue(add = 1, custom_message){ // By default 1 character is made visible
var temp_message;
if(typeof custom_message === "undefined") {
temp_message = message;
}
else {
temp_message = custom_message;
}
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(temp_message.substr(0, len)); // Make the change
if (len < temp_message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on page load
$("#button1").hide();
<!DOCTYPE=HTML>
<html>
<head>
<title>Sandbox</title>
<link rel="stylesheet" href="mainstyle.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p id="pid"></p>
<button id="button1">Ooh, here's one! Click to see what it does!</button>
</body>
</html>
You can simply ask for another parameter in dialogue() for the new message. You can then create an array of messages, from which you can choose whatever message you'd like to pass. This will ensure that all your messages are saved.
var messages = ["Message one", "Message two", "Message three"];

How to Manipulate Image After Uploaded To Canvas (FabricJS)

I've been racking my brain on this for a while now, but I can't seem to rotate the image on Canvas after uploading. Here's the code sample in question:
// INITIATE & DRAW CANVAS
var canvas = new fabric.Canvas('CanvasArtArtboard');
// CREATE OVERLAY GRIDS
canvas.setOverlayColor('rgba(205, 173, 64, 0.6)', canvas.renderAll.bind(canvas));
// UPLOAD USER IMAGE TO CANVAS
document.getElementById('imgLoader').onchange = function handleImage(e) {
var reader = new FileReader();
reader.onload = function (event) {
var imgObj = new Image();
imgObj.src = event.target.result;
imgObj.onload = function () {
// start fabricJS stuff
var image = new fabric.Image(imgObj);
image.set({
originX: 'left',
originY: 'top',
padding: 0,
cornersize: 10,
scaleX: 0.4,
scaleY: 0.4,
});
//image.scale(getRandomNum(0.1, 0.25)).setCoords();
canvas.add(image);
image.sendToBack();
canvas.renderAll();
//end fabricJS stuff
}
}
reader.readAsDataURL(e.target.files[0]);
}
// ROTATE UPLOADED IMAGE
$("#angle-control").click(function(){
var curAngle = canvas.item(0).getAngle();
canvas.item(0).setAngle(curAngle+15);
canvas.renderAll();
});
I've looked at the following example:
Add image from user computer to canvas (Stack OverFlow Question)
http://jsfiddle.net/PromInc/3efe2x9j/
Once the user uploads their image and clicks the "rotate 90*" button, I'm looking for the image to then rotate.
EDIT: Apologies, here is my JSFiddle in it's current state: http://jsfiddle.net/darnellcreates/oq6htzew/3/
I had a look at your fiddle and there is a lot wrong. The code for rotating is ok (sort of) but the page is full of problems.
First there is no JQuery and you are using the JQuery $ function as selector to access DOM elements. I do not see the point in using jQuery if all you are doing is using it as a selector, you use document.getElementById in some parts and $ in others. This totally negates any benefit Jquery would provide.
Second you are duplicating DOM element ID's (This is a strict no no) DOM ID's must be unique to the page.
To get you on your way here are a few changes,
Change the slider element in the HTML to
<input type="range" id="angleControl1" value="0" min="-90" max="90">
Note that the id is unique to the page. I have also avoided the naming convention (did not make it angle-control1) as this is a simple page and I will take some shortcuts and access the DOM elements directly in JavaScript.
Next remove the click handler for the range slider and replace it with a mouse move event listener.
// keep a copy of the last value so there is no needless rotates
var lastValue = angleControl1.value; // no need to search the node tree.
// you can access elements directly by
// their ID.
// Listen to the mousemoves of the range control. Click, change will
// only update at the end of the mouse down up process. This does not
// provide good user feedback
angleControl1.addEventListener("mousemove",function(){
if(this.value !== lastValue){ // only update the angle if it has changed
// set the angle to the absolute angle rather than relative as you had it
canvas.item(0).setAngle(Number(this.value)); // Number() is me being pedantic
canvas.renderAll(); // rerender
lastValue = this.value; // remember the last value
}
}); // all done for this function
Note. Direct DOM element access can be problematic in some situations (if you duplicate ID's, use that id as a variable before the DOM has loaded, your page is sharing the global namespace, or someone is using an old netscape browser) but for simple pages it is the easiest and safest way (If someone has netscape they can't run the page anyway because there is not canvas element).
Also remove the other Jquery calls $() as they cause the page to crash and stop everything from running.
You need to use the DevTools in these types of situations. It will show you all the errors and give you warnings (FireFox) for bad habits. To access the DevTool hit f12 on most browsers.The DevTools console will list errors and provide a link directly to the code where it is happening. You can also step through the code line by line so you can see what is going on.
i updated your jsfiddle ,to work both the rotate button 90o and the range bar,
just in case you need it.
link: http://jsfiddle.net/rqevmp3y/1/
$("#angle-control").on('click', function (event) {
console.log('rotate');
var curAngle = canvas._objects[0].getAngle();
canvas._objects[0].setAngle(curAngle+90);
canvas.renderAll();
});
$("#rangeControl").on('change', function (event) {
console.log('rotate from range',$(this).val());
var curAngle = canvas._objects[0].getAngle();
canvas._objects[0].setAngle(curAngle+parseInt($(this).val()));
canvas.renderAll();
});

OL3 - Trouble detecting clicks on features

My aim is to create a map for my work in OpenLayers 3 with several layers. One takes a basic feed from OpenStreetMaps. Another will be a transparent layer showing outlines of regions (not done yet). The third one, which is the one I'm having trouble with, shows a series of icons representing indivudal sites of interest on the map. I load the sites from a JS data stucture included as a separate script file. I have managed to get my code to add features that appear at the correct lat/lon. My next step is to get a HTML box (div) to appear in front of the map when they click on an icon (to display details of the site). However, I cannot get this to work. Apologies for my noobish coding, but it's really got me stumped and I'd really appreciate any help.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="ol.css" type="text/css">
<link rel="stylesheet" href="map.css" type="text/css">
<script src="ol.js" type="text/javascript"></script>
<script src="sites.js" type="text/javascript"></script>
<script type="text/javascript">
function buildLayerFromSites(allSites)
// allSites is just an array of data structures
{
var count, site;
// for each site in the array, make a feature
for (count=0;count<allSites.length;count++)
{
geom = new ol.geom.Point(ol.proj.fromLonLat([allSites[count].longitude,allSites[count].latitude]));
site = new ol.Feature(geom);
site.Name = allSites[count].siteName; // <-- can I assign further details in the structure like this?
var siteStyle = new ol.style.Style({
image: new ol.style.Icon ({
src: 'icon-blue.png',
scale: 0.1,
opacity: 0.9,
})
})
site.setStyle(siteStyle);
siteFeatures[count] = site;
}
siteSource = new ol.source.Vector ({
features: siteFeatures
})
siteLayer = new ol.layer.Vector({
source: siteSource
});
map.addLayer(siteLayer);
}
</script>
<title>Map</title>
</head><body>
...
<div id="map" class="map">
...
<script type="text/javascript">
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([115.51, -31.57]),
zoom: 8
})
});
buildLayerFromSites(includeSites); // adds the second layer with the sites
// click event handler, basically same as http://openlayers.org/en/v3.12.1/examples/icon.html
map.on('click', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature,layer) {
return feature;
});
if (feature) {
console.log("feature got clicked on"); // do stuff
} else {
console.log("didn't click a feature");
}
});
</script>
</body>
</html>
The map and the icons load, but when I click the icons, no matter how zoomed in or out I am, it doesn't detect a match with the icon I'm clicking on. The thing is, the event handler code is the same as in the official example, which make me think it's the way I'm creating the features. On the other hand, the features display fine as icons, its just the click event that doesn't seem to work. Similar weird stuff happens for the mouse cursor change from the example. I sorta guess it's because I don't understand the way the data/functions are structured in OL, and I find the documentation doesn't fully explain how it all works, so I've been writing snippets like this trying to take a look:
var myLayers = map.getLayers();
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
var mySource = myLayers.getSource;
console.log("source" + mySource.getProjection); // doesn't work
var features = mySource.getFeatures; // says mySource is undefined
Of course it fails totally.
How do I get it to detect the clicks on the icons/features so I can tell a div to appear and display my data at the right time? Am I doing this right? What don't I understand about the way the data/functions work? I'm a big JS noob so I hope I'm not doing something stupid, but it's causing me massive problems and I could really use your help! Thanks all!
If someone else comes upon this issue: my solution was changing the Feature icon PNG image to not have transparency (no color).
If your image is PNG and has transparency,
where it is transparent it won't be clickable
(I'm on OpenLayers v5). For example, I had an image that was a circle and it was transparent inside and just colored on the outside border.
When I changed the image to something without transparency in the middle (it can still be PNG of course) everything was okay. It seems this is how OpenLayers handles pixels for images - if the map can be seen through the image, it's not a 'hover' even though you are on a Feature.
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
It means you're trying to print the function itself, not the result of it's execution. Should be
console.log("keys - " + myLayers.getKeys())
Same with
mySource.getProjection();
mySource.getFeatures();
Usually if the attribute name starts with "get" and is written in camelCase - it's a function. Keep that hint in mind :)
After long hours staring at my screen and hitting my head on my desk, I've stumbled across the solution to my problem. It's a little obscure but mind-numbingly simple/stupid/silly. I thought I'd post the solution here for others.
I wrote a couple of lines of code to resize the viewport when the page is loading and again when the window is resized, so the map would adjust to the available user's browser window space. Unfortunately, as far as I can work out, OL doesn't know about it so the icons and the features are no longer in the same place after the resize. I didn't know this though, until after countless hours I was randomly clicking and resizing it detected a feature click.
Luckily there is an easy way to solve it once you figure out this is the problem. Basically just add map.updateSize() to the part of your code that resizes the viewport. So far this seems to have solved the problem.
The functions is only work on pixel of features so to solve this you can add some style with your image with fill with something like rgb(255,0,0,0.001) propert. I have tried it and i worked for me.

How to determine when a canvas has finished loading

So I was making a rather large canvas that was filled with 1x1 pixels, and I wanted to make a loading screen for it. The problem after the loop filling the canvas finishes, it alerts that it has finished loading, but the canvas has not actually changed. I made a jsfiddle here to demonstrate. How do I actually find out when the canvas has actually loaded using javascript or Jquery, and what is causing this behavior?
var ctx=document.getElementById('canvas').getContext('2d');
for(var x=1;x<600;x++){
for(var y=1;y<600;y++){
var color= '#' + Math.floor (Math.random() * 16777215).toString(16);
ctx.fillStyle=color;
ctx.fillRect(x,y,1,1);
}
}
alert('done!');
Since you said jquery was ok, just fire a custom event when the loops are done looping.
Any code that needs the canvas fully loaded can go in the event handler.
// Listen for custom CanvasOnLoad event
$(document).on( "CanvasOnLoad", canvasOnLoadHandler );
var ctx=document.getElementById('canvas').getContext('2d');
for(var x=1;x<600;x++){
for(var y=1;y<600;y++){
var color= '#' + Math.floor (Math.random() * 16777215).toString(16);
ctx.fillStyle=color;
ctx.fillRect(x,y,1,1);
}
// fire CanvasOnLoad when the looping is done
if(x>=599){
$.event.trigger({
type: "CanvasOnLoad"
});
}
}
console.log("...follows the for loops.");
// handle CanvasOnLoad knowing your canvas has fully drawn
function canvasOnLoadHandler(){
console.log("canvas is loaded");
}
It is just like a load-progress animation. Updating/advancing that animation from a running function usually doesn't work right away (screen updates when that function finishes).
Your canvas-code is (automatically) wrapped in a onload function: window.onload=function(){ /*your code here*/ };.
The last line of that function is alert('done!');, so naturally you'll get the alertbox before the screen updates and you see the noize.
One solution is to first one set and display's a loading-image, then using a setTimeOut (say 30ms) one renders the canvas, ending that canvas-function with another setTimeOut to remove the loading-image again.
Note: as you probably know, your code will generate (a lot of) hex-colors like #3df5 and #5d8a6, both of which aren't valid colors! Also you used 16777215, but Math.random() needs to be multiplied by 16777216. To fix this, you might want to try:
color='#'+((Math.random()+1)*16777216|0).toString(16).substr(1);
Here is some good read about random colors.
See this JSFiddle result as an example.
Hope this helps.

Javascript onload event to resize image help

This might be a simple one for you guys, im learning Javascript and have hit a problem. I am trying to have the script resize a particular image on the page when onload is called like so:
<script type="text/javascript">
function resizeSampleImage()
{
document.getElementById("sampleImage").style.height = (document.body.clientWidth) * 0.2;
}
</script>
...
<body onload = "resizeSampleImage();" >
...
<img id="sampleImage" src="Images/BP snip.jpg" alt="BuildingPeople.uk.com" />
apologies, forgot to mention that is doesn't work! after loading the page in multiple browsers the image stays it native size and the error consoles say they fail to parsing value for height. Declaration dropped.
I have tried lots of different ways but cannot seem to get it to work.
(document.body.clientWidth) * 0.2 will give you an integer. The CSS height property accepts a length.
Add some units.
There is a live example at http://jsbin.com/uyihe4
You can see the zoom effect when loading. I have only changed the
document.getElementById("sampleImage").style.height
to
document.getElementById("sampleImage").height
Well, your problem is that the images may not be ready when you apply the styles to them.
Use this function instead (note it takes a DOM object):
var imgLoader = function(domImg) {
var imgObj = new Image();
imgObj.onload = function()
{
// apply styles
domImg.style.height = "100px";
}
imgObj.src = domImg.src;
}
Though I haven't figure out the problem. But the following code snippet works for me (Tested in Chrome 8.0 and IE9.0Beta).
<html>
<head>
<title>test</title>
<script type="text/javascript">
function resizeSampleImage()
{
document.getElementById("sampleImage").style.height = (document.body.clientWidth) * 0.5;
}
</script>
</head>
<body onload="resizeSampleImage();">
<img id="sampleImage" src="http://news.mydrivers.com/Img/20101217/S03405153.jpg">
</body>
</html>

Categories