Related
I am working on a project where i need to create resizable rectangles on my image.
I found this codepen useful:
https://codepen.io/taye/pen/xEJeo
the problem is when i write same code in my codepen, its not working same. see
here
https://codepen.io/KushalParikh/pen/rJOeOy.
Can you please tell me what I am missing. I am new to javascript and svg.
my code
var svgCanvas = document.querySelector('svg'),
svgNS = 'http://www.w3.org/2000/svg',
rectangles = [];
function Rectangle (x, y, w, h, svgCanvas) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stroke = 5;
this.el = document.createElementNS(svgNS, 'rect');
this.el.setAttribute('data-index', rectangles.length);
this.el.setAttribute('class', 'edit-rectangle');
rectangles.push(this);
this.draw();
svgCanvas.appendChild(this.el);
}
Rectangle.prototype.draw = function () {
this.el.setAttribute('x', this.x + this.stroke / 2);
this.el.setAttribute('y', this.y + this.stroke / 2);
this.el.setAttribute('width' , this.w - this.stroke);
this.el.setAttribute('height', this.h - this.stroke);
this.el.setAttribute('stroke-width', this.stroke);
}
interact('.edit-rectangle')
// change how interact gets the
// dimensions of '.edit-rectangle' elements
.rectChecker(function (element) {
// find the Rectangle object that the element belongs to
var rectangle = rectangles[element.getAttribute('data-index')];
// return a suitable object for interact.js
return {
left : rectangle.x,
top : rectangle.y,
right : rectangle.x + rectangle.w,
bottom: rectangle.y + rectangle.h
};
})
.inertia({
// don't jump to the resume location
// https://github.com/taye/interact.js/issues/13
zeroResumeDelta: true
})
.restrict({
// restrict to a parent element that matches this CSS selector
drag: 'svg',
// only restrict before ending the drag
endOnly: true,
// consider the element's dimensions when restricting
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
.draggable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.x += event.dx;
rectangle.y += event.dy;
rectangle.draw();
}
})
.resizable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.w = Math.max(rectangle.w + event.dx, 10);
rectangle.h = Math.max(rectangle.h + event.dy, 10);
rectangle.draw();
}
});
interact.maxInteractions(Infinity);
for (var i = 0; i < 5; i++) {
new Rectangle(50 + 100 * i, 80, 80, 80, svgCanvas);
}
svg {
width: 100%;
height: 240px;
background-color: #2e9;
-ms-touch-action: none;
touch-action: none;
}
.edit-rectangle {
fill: #92e;
stroke: black;
}
body { margin: 0; }
<svg>
</svg>
Indeed your pen is not working properly. You forgot to add interact.js to your pen as necessary javascript resource. Just go to Settings|Javascript and add something like https://c4d6f7d727e094887e93-4ea74b676357550bd514a6a5b344c625.ssl.cf2.rackcdn.com/interact-1.1.1.min.js
You need to import
https://c4d6f7d727e094887e93-4ea74b676357550bd514a6a5b344c625.ssl.cf2.rackcdn.com/interact-1.1.1.min.js
in your JS
I have a canvas that I am drawing on top of a OpenLayers map. The canvas is colored in gradient colors and now I am trying to add text on top of it.
However the text seems to be stretched making it completely unreadable.
function createSpeedBar(min, max, speeds) {
//List of integer speeds
var fullSpeeds = [];
//List of unique speed values (no duplicates)
var uniqueSpeeds = [];
//Filling fullSpeeds using minimum and maximum values
for (i = min; i <= max; i++) {
fullSpeeds.push(i);
}
//Filling uniqueSpeeds with unique values
$.each(speeds, function (i, el) {
if ($.inArray(el, uniqueSpeeds) === -1) uniqueSpeeds.push(el);
});
//Sorting uniqueSpeeds (low to high)
uniqueSpeeds = uniqueSpeeds.sort(function (a, b) {
return a - b;
});
//Getting canvas element
var canvas = document.getElementById("speedList");
var context = canvas.getContext("2d");
context.rect(0, 0, canvas.width, canvas.height);
var grd = context.createLinearGradient(0, 0, 0, 170);
var hslMin = 0;
var hslMax = 240;
//Defining hslColors using uniqueSpeeds
var hslValues = uniqueSpeeds.map(function (value) {
if ($.inArray(value, fullSpeeds)){
return {
h: Math.ceil(((value - min) / (max - min)) * (hslMax - hslMin) + hslMin),
s: 100,
l: 50,
full: true,
speed: value
};
} else {
return {
h: Math.ceil(((value - min) / (max - min)) * (hslMax - hslMin) + hslMin),
s: 100,
l: 50,
full: false
};
};
});
var count = 1;
var length = hslValues.length;
//Gradient coloring using hslColors
hslValues.forEach(function (value) {
var color = 'hsl(' + value.h + ',' + value.s + '%,' + value.l + '%)';
grd.addColorStop(count / length, color)
count += 1;
});
context.fillStyle = grd;
context.fill();
//Setting up coloring and drawing of text
count = 1
var height = canvas.height;
var width = canvas.width;
var elementHeight = height / length;
//Drawing text on canvas
hslValues.forEach(function (value) {
context.font = "12px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "black";
if (value.full === true) {
context.fillText(value.speed, width / 2, ((elementHeight / 2) + elementHeight * count));
}
count += 1;
});
}
As it might be clear I am trying to create a bar displaying the intensities of the speed on the map where I have colored some markers. However the text on the canvas comes out like this:
Right now I have made the height of the canvas inherit the height of the map which is 500. The width of the canvas is set manually using css:
html
<div id="map" class="map">
<canvas id="speedList"></canvas>
</div>
css
#map {
position: relative;
height: 500px;
}
#speedList {
position: absolute;
right: 10px;
top: 0;
bottom: 0;
width: 20px;
z-index: 1000;
height: inherit;
margin: 0;
padding: 0;
}
I am currently working on a Fiddle, but it takes a little time to reproduce, and I bet the issue is not that big, so hopefully someone knows how to fix it before I finish the Fiddle.
The main problem here is the CSS adjustment of the width and height of the canvas element.
If it helps to understand the problem, think of <canvas> the same way you would think of <img/>, if you take an img, and give it a width and height of 50 x 500, it would stretch too.
The fix is to ensure that you set the width an height of the canvas element itself, before it processes it's content, like this:
<canvas id="speedList" width="20" height="500"></canvas>
You then also need to make sure your remove the width and height properties inside your CSS, like this:
#map {
position: relative;
height: 500px;
}
#speedList {
position: absolute;
right: 10px;
top: 0;
bottom: 0;
z-index: 1000;
margin: 0;
padding: 0;
}
I have a canvas script, with a dynamic of data. I want to add a link to share the website to facebook:
https://gyazo.com/c1fd1fe956fddba27b48907dc0e9de0a
The icons are part of the image I have not generated them via canvas, now if I listen for a click for co-ords it won't work because it'll look for clicks on the first canvas part aswell.... How can I go about making those icons part of the image clickable....
Part that makes the menu:
ig.module("game.entities.gameover").requires("impact.entity", "game.entities.button-gameover").defines(function() {
var b = new ig.Timer;
EntityGameover = ig.Entity.extend({
size: {
x: 302,
y: 355
},
type: ig.Entity.TYPE.B,
animSheet: new ig.AnimationSheet("media/graphics/game/gameover.png", 301, 352),
zIndex: 900,
globalAlpha: 0.1,
closeDialogue: !0,
init: function(c, d, g) {
this.parent(c, d, g);
this.addAnim("idle", 1, [0]);
this.currentAnim = this.anims.idle;
this.tween({
pos: {
x: 89,
y: 120
}
}, 0.5, {
easing: ig.Tween.Easing.Back.EaseInOut
}).start();
this.storage = new ig.Storage;
this.storage.initUnset("highscore-CTF", 0);
this.storage.initUnset("highscore-CTF2", 0);
this.storage.initUnset("highscore-CTF3", 0);
ig.global.score > this.storage.get("highscore-CTF") ? (this.storage.set("highscore-CTF3", this.storage.get("highscore-CTF2")), this.storage.set("highscore-CTF2", this.storage.get("highscore-CTF")), this.storage.set("highscore-CTF", ig.global.score), this.storage.initUnset("highscore-CTF2", 0), this.storage.initUnset("highscore-CTF3", 0)) : ig.global.score > this.storage.get("highscore-CTF2") ?
(this.storage.set("highscore-CTF3", this.storage.get("highscore-CTF2")), this.storage.set("highscore-CTF2", ig.global.score), this.storage.initUnset("highscore-CTF2", 0), this.storage.initUnset("highscore-CTF3", 0)) : ig.global.score > this.storage.get("highscore-CTF3") && this.storage.set("highscore-CTF3", ig.global.score);
this.storage.initUnset("total-CTF", 0);
this.storage.set("total-CTF", this.storage.get("total-CTF") + ig.global.score);
ig.game.spawnEntity(EntityButtonGameover, 23, 700, {
buttonID: 1
});
ig.game.spawnEntity(EntityButtonGameover,
220, 700, {
buttonID: 2
});
ig.game.spawnEntity(EntityButtonGameover, 390, 700, {
buttonID: 3
});
b.set(0.3)
},
update: function() {
this.parent()
},
draw: function() {
this.ctx = ig.system.context;
this.closeDialogue ? (this.ctx.save(), this.ctx.fillStyle = "#000000", this.ctx.globalAlpha = this.globalAlpha, this.ctx.fillRect(0, 0, 480, 640), this.ctx.restore(), this.globalAlpha = 0.7 <= this.globalAlpha ? 0.7 : this.globalAlpha + 0.01) : this.closeDialogue || (this.ctx.save(), this.ctx.fillStyle = "#000000", this.ctx.globalAlpha = this.globalAlpha, this.ctx.fillRect(0,
0, 480, 640), this.ctx.restore(), this.globalAlpha = 0.1 >= this.globalAlpha ? 0 : this.globalAlpha - 0.05);
this.parent();
this.ctx.font = "30px happy-hell";
this.ctx.fillStyle = "#5b2a0b";
this.ctx.textAlign = "center";
this.ctx.fillText(_STRINGS.UI.Best, this.pos.x + 70, this.pos.y + 180);
this.ctx.fillText(_STRINGS.UI.Score, this.pos.x + 70, this.pos.y + 260);
//share
this.ctx.font = "30px happy-hell";
this.ctx.fillStyle = "#ffffff";
this.ctx.textAlign = "left";
this.ctx.fillText(this.storage.getInt("highscore-CTF"), this.pos.x + 140, this.pos.y + 180);
this.ctx.fillText(ig.global.score, this.pos.x + 140, this.pos.y + 260)
},
closeDialogueFunc: function() {
this.closeDialogue && (this.tween({
pos: {
x: 89,
y: -600
}
}, 0.5, {
easing: ig.Tween.Easing.Back.EaseInOut
}).start(), this.closeDialogue = !1)
}
})
});
A simple, versatile way to add menus to canvas graphics is to simply overlay an absolutely positioned DOM structure. Your browser has already all event handling build-in, there is no need to reinvent the wheel.
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(0, 155, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height)
#container {
position: relative;
display: inline-block;
}
#menu {
position: absolute;
text-align: center;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
#menu a {
padding: 15px;
font-size: 50px;
line-height: 100px;
color: black;
text-shadow: 2px 2px 5px white;
}
#menu a:hover {
color: white;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" />
<div id='container'>
<canvas id='canvas' width='400' height='100'></canvas>
<div id='menu'>
<a href='http://facebook.com'><i class='fa fa-facebook-official'></i></a>
<a href='http://twitter.com'><i class='fa fa-twitter'></i></a>
<a href='http://whatsapp.com'><i class='fa fa-whatsapp'></i></a>
</div>
</div>
Your browser is capable of rendering such overlay menus very quickly. You should use CSS to style your overlay menu links or buttons.
I have managed to click on a certain element in a canvas.
I have tried to explain with comments what it does.
I have made 3 limits as shown in the image below.
And I'm comparing only with x value, if it's in between these limits. It can be more complex so getCursorPosition() method returns an object with x and y components, just in case if you need to make more comparisons.
https://jsfiddle.net/_jserodio/asa10pye/
var canvas;
var ctx;
// first get your canvas
canvas = document.getElementById('canvas');
canvas.width = 253;
canvas.heigth = 68;
// assign the context
ctx = canvas.getContext("2d");
// asign click event to the canvas
addEventListener("click", listener, false);
function listener(e) {
// if you have 3 buttons
var position = getCursorPosition(e);
var limit1 = canvas.width / 3;
//console.log("limit1: " + limit1);
var limit2 = canvas.width * 2 / 3;
//console.log("limit2: " + limit2);
var limit3 = canvas.width;
//console.log("limit3: " + limit3);
if (position.x < limit1) {
console.log("go to facebook");
//window.open("http://www.facebook.com");
} else if (position.x < limit2) {
console.log("go to twitter");
//window.open("http://www.twitter.com");
} else if (position.x < limit3) {
console.log("go to whatsapp");
//window.open("http://www.whatsapp.com");
}
//console.log("\nx" + position.x);
//console.log("y" + position.y);
}
function getCursorPosition(event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var data = {
x: x,
y: y
};
return data;
}
// load image from data url
var imageObj = new Image();
imageObj.onload = function() {
ctx.drawImage(this, 0, 0);
};
imageObj.src = 'https://justpaste.it/files/justpaste/d307/a11791570/file1.png';
<canvas id='canvas' width="253" height="68">
</canvas>
Bonus!
Here you have a demo I made using this. demo (Draughts/checkers).
You can check the entire code here if you want.
You have several operations in this case.
Option 1:
You'll need to remember the "bounding area" of the three buttons. Anytime the canvas receives a "click", get the click position (How do I get the coordinates of a mouse click on a canvas element?). Once you get the click position. Detect if said click occurs within the bounding area of the button. If it does, use window.open to go there.
Option 2: Similar to Option 1, but instead of remembering a "bounding area", create a separate hidden canvas of the same size as your image with the backgrounds black ('#000000') and the button given distinctive colors (for example, red for Facebook, green for Twitter, and blue for Hangout?).
Then, similar to Option 1, get the click position relative to the canvas. Then use ctx.getImageData(sx, sy, sw, sh) on the context of the hidden canvas layer. If the value you get back is red, then user clicked on Facebook, if green, Twitter, and if blue, Hangout.
I used interact.js library to write this piece of code which works absolutely fine standalone on chrome, firefox and w3schools "Try it Yourself" (doesn't work on Edge and IE for some reason). The problem is that when I call a template.phtml with this code inside from the layout.xml, the magento renders it only once, thus the user is not allowed to resize the cubes.
<!-- CSS -->
<style type="text/css">
svg {
width: 100%;
height: 300px;
background-color: #CDC9C9;
-ms-touch-action: none;
touch-action: none;
}
.edit-rectangle {
fill: black;
stroke: #fff;
}
body { margin: 0; }
</style>
<!-- Content -->
<br>
<svg>
</svg>
<br>
<button onclick="location.href = 'square';" id="previousbutton">Go back</button>
<button onclick="location.href = 'squaresection';" style="float:right" id="nextButton">Proceed to next step</button>
<br>
<br>
<script type="text/javascript" src="interact.js">
</script>
<!-- JavaScript -->
<script type="text/javascript">
var svgCanvas = document.querySelector('svg'),
svgNS = 'http://www.w3.org/2000/svg',
rectangles = [];
labels = [];
rectNumb = 5;
function Rectangle (x, y, w, h, svgCanvas) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stroke = 0;
this.el = document.createElementNS(svgNS, 'rect');
this.el.setAttribute('data-index', rectangles.length);
this.el.setAttribute('class', 'edit-rectangle');
rectangles.push(this);
this.draw();
svgCanvas.appendChild(this.el);
}
function Label (x, y, text, svgCanvas){
this.x = x;
this.y = y;
this.text = text;
this.el = document.createElementNS(svgNS, 'text');
labels.push(this);
this.draw();
svgCanvas.appendChild(this.el);
}
Label.prototype.draw = function () {
this.el.setAttribute('x', this.x);
this.el.setAttribute('y', this.y);
this.el.setAttribute('font-family', "Verdana");
this.el.setAttribute('font-size', 14);
this.el.setAttribute('fill', "black");
this.el.innerHTML = this.text;
}
Rectangle.prototype.draw = function () {
this.el.setAttribute('x', this.x + this.stroke / 2);
this.el.setAttribute('y', this.y + this.stroke / 2);
this.el.setAttribute('width' , this.w - this.stroke);
this.el.setAttribute('height', this.h - this.stroke);
this.el.setAttribute('stroke-width', this.stroke);
}
interact('.edit-rectangle')
// change how interact gets the
// dimensions of '.edit-rectangle' elements
.rectChecker(function (element) {
// find the Rectangle object that the element belongs to
var rectangle = rectangles[element.getAttribute('data-index')];
// return a suitable object for interact.js
return {
left : rectangle.x,
top : rectangle.y,
right : rectangle.x + rectangle.w,
bottom: rectangle.y + rectangle.h
};
})
/*
.draggable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.x += event.dx;
rectangle.y += event.dy;
rectangle.draw();
}
})
*/
.resizable({
onstart: function (event) {},
onmove : function (event) {
if (event.target.getAttribute('data-index') > 0)
{
// Main Rect
var rectangle = rectangles[event.target.getAttribute('data-index')];
var rectangle2 = rectangles[event.target.getAttribute('data-index') - 1];
if (rectangle.w - event.dx > 10 && rectangle2.w + event.dx > 10){
rectangle.x += event.dx;
rectangle.w = rectangle.w - event.dx;
rectangle2.w = rectangle2.w + event.dx;
}
rectangle.draw();
rectangle2.draw();
var label = labels[event.target.getAttribute('data-index')];
var label2 = labels[event.target.getAttribute('data-index') - 1];
label.text = rectangle.w + " mm";
label2.text = rectangle2.w + " mm";
label.x = rectangle.x + rectangle.w / 4;
label2.x = rectangle2.x + rectangle2.w / 4;
label.draw();
label2.draw();
}
},
onend : function (event) {},
edges: {
top : false, // Disable resizing from top edge.
left : true,
bottom: false,
right : false // Enable resizing on right edge
},
inertia: false,
// Width and height can be adjusted independently. When `true`, width and
// height are adjusted at a 1:1 ratio.
square: false,
// Width and height can be adjusted independently. When `true`, width and
// height maintain the aspect ratio they had when resizing started.
preserveAspectRatio: false,
// a value of 'none' will limit the resize rect to a minimum of 0x0
// 'negate' will allow the rect to have negative width/height
// 'reposition' will keep the width/height positive by swapping
// the top and bottom edges and/or swapping the left and right edges
invert: 'reposition',
// limit multiple resizes.
// See the explanation in the #Interactable.draggable example
max: Infinity,
maxPerElement: 3,
});
interact.maxInteractions(Infinity);
var positionX = 50,
positionY = 80,
width = 80,
height = 80;
for (var i = 0; i < rectNumb; i++) {
positionX = 50 + 82 * i;
new Rectangle(positionX, positionY, width, height, svgCanvas);
}
for (var i = 0; i < rectNumb; i++) {
positionX = 50 + 82 * i;
new Label(positionX + width/4, positionY + height + 20, width +" mm", svgCanvas);
}
</script>
Any suggestions of what I could do to implement this code into magento would be much appreciated.
Magento did not render the code only once. The problem was that canvas event listener always assumed that pointer coordinates were wrong. Since canvas is the first element of the page(because it is the first element in that .phtml file), event listener assumed it will be displayed at the top, but that was not the case because of the way magento page rendering works.
This issue was resolved simply by measuring the height of content above canvas and just mathematically subtracting that from pointers position before passing it to event listener.
The problem with this solution is that it works only for single page or with multiple pages that have the same height of content above canvas(=>same design). If anyone knows a way in which person would not need to "recalculate" the height for every single page that has different design, sharing knowledge would be much appreciated.
I'm trying to resize/scale an image using Raphael.js's built in drag method, but I'm getting some weird behavior.
Here is the jsfiddle: http://jsfiddle.net/charleshimmer/5pdyy/1/
Use the right or bottom right corner to resize the image. You will see some weird behavior with it jumping and skipping using the scale method. Anybody have any idea why?
I can get it to resize smoothing by updating the image's width and height, but then the aspect ratio is off. Using image.scale, the aspect ratio is maintained, but then it jumps all over the place.
HTML
<html>
<head>
<title>Photo Test</title>
</head>
<body>
<div id="editor"></div>
<img id="image"
src="http://www.pyrblu.com/assets/launchpad_resources/demo.jpg"
style="display:none"
>
</body>
</html>
CSS
svg
{
border: 1px solid red;
background:#fff;
border-radius: 45px;
}
JavaScript
var Editor = {},
ctFactor = 7;
// create Raphael canvas
Editor.paper = Raphael('editor', 582, 514.8);
// wait for image to load
$("#image").load(function(){
Editor.image = Editor.paper.image("http://www.pyrblu.com/assets/launchpad_resources/demo.jpg", 25, 25, 282, 465.2);
Editor.image.drag(Editor.dragging, Editor.dragStart, Editor.dragEnd);
Editor.image.ready = true;
Editor.image.mousemove(function (e) {
// only do this if the user isn't currently moving / resizing image
if( ! this.ready){
return;
}
var side = Editor.sideDection(e, this);
// if the user's mouse is along the edge we want resize
if(side){
Editor.image.state = 'resizable';
}
// else it's towards the middle and we want to move
else{
Editor.image.state = 'movable';
}
var cursor = (side) ? side + '-resize' : 'move';
this.attr('cursor', cursor);
});
});
Editor.sideDection = function(event, ct){
// check north side
var directions = {
n: Math.abs(event.offsetY - ct.attr('y')) <= ctFactor,
s: Math.abs(event.offsetY - (ct.attr('height') + ct.attr('y'))) <= ctFactor,
e: Math.abs(event.offsetX - (ct.attr('width') + ct.attr('x'))) <= ctFactor,
w: Math.abs(event.offsetX - ct.attr('x')) <= ctFactor
},
side = '';
// loop through all 4 sides and concate the ones that are true
for(var key in directions) {
if(directions.hasOwnProperty(key)){
if(directions[key]){
side = side + key;
}
}
}
return side;
};
Editor.dragStart = function () {
console.log('at start');
// grab original x, y coords
this.ox = this.attr("x");
this.oy = this.attr("y");
// toggle user is doing something
// so other actions are blocked
this.ready = false;
this.animate({opacity: .65}, 500, ">");
};
Editor.dragging = function (dx, dy, x, y, e) {
console.log('at dragging');
if(this.state === 'movable'){
// this does the actual moving of the object
this.attr({x: this.ox + dx, y: this.oy + dy});
}
// we are resizing then
else{
var diff = (x - this.ox) - this.attr('width'),
xratio = 1 + diff / this.attr('width'),
yratio = 1 + diff / this.attr('height');
console.log('diff: ', diff, 'xratio: ', xratio, 'yratio: ', yratio);
//resize image, update both height and width to keep aspect ratio
// this.attr({
// 'width': this.attr('width') * xratio,
// 'height': this.attr('height') * yratio
// });
this.scale(xratio, xratio, 0, 0);
//console.log('h: ', this.attr('height'), 'w: ', this.attr('width'), 'r', this.attr('width') / this.attr('height'));
}
};
Editor.dragEnd = function () {
this.ready = true;
this.animate({opacity: 1}, 500, ">");
};