Im trying to figure out what im dooing wrong. I have a hmtl5 canvas inside a bootstrap modal. The canvas is a selecting game where you select objects inside the canvas and manipulate them.
Unfortunately it works allright in the center of the 600x600px Canvas but im getting a wierd offset when im trying to get the coordinates in the corner of the picture. The offset is getting bigger as you get closer to the corners
Here's my code.
Thank you very much in advance!
Javascript:
document.addEventListener('DOMContentLoaded', (ev) => {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 600;
let imgObj = new Image();
imgObj.onload = function() {
let w = canvas.width;
let nw = imgObj.naturalWidth; //1350
let nh = imgObj.naturalHeight; //900
console.log(nw);
console.log(nh);
let aspect = nw / nh;
let h = w / aspect;
console.log('height', h)
canvas.height = h;
soldTilesDisplay.textContent = totalTiles-soldTiles;
ctx.drawImage(imgObj, 0, 0, w, h);
rectangulize_with_IDs();
fillarraywithrandomTiles(soldTiles);
//greyscaleOriginal();
//ctx.drawImage(imgObj, dx, dy);
//ctx.drawImage(imgObj, dx, dy, dw, dh);
//ctx.drawImage(imgObj, sx, sy, sw, sh, dx, dy, dw, dh);
};
function getMousePosition(canvas, event) {
let rect = canvas.getBoundingClientRect();
let x = event.clientX - Math.floor(rect.left);
let y = event.clientY - Math.floor(rect.top);
storePixelArray(x, y); // originale "Store in Array funktion"
store_rect(x,y);
clickcounter = clickcounter + 1;
console.log("Exact Coordinates: X: " + x,
" Y: " + y, "CLick Counter: "+ clickcounter);
}
Html:
<div>
<!-- Bootstrap Modal here!-->
<div class="row">
<div class="col-lg-4">
<!-- Bootstrap Modal here!-->
<div class="modal fade" id="myModal1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h3>WIN!</h3>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<canvas id="canvas" style="background-image: url('/images/Berge.png')"></canvas>
</div>
<div class="col-md-6 justify-content-center">
<div class="card text-center" style="width: 30rem;">
<div class="card-body">
<h5 class="card-title" id="Punktestand"> Card Title
Score:
</h5>
<p class="card-text" id="paragraphImModal">With supporting
text below as a natural
lead-in to additional content.</p>
<button type="button" class="btn btn-default" id="usePointsPixelButton">
Select Pixel first</button>
<br>
<h5 class="selectedPixelAmount" id="AmountOfPixels"> </h5>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-lg" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
I found the problem after running into the same Issue.
It is because the class .modal-dialog and every subclass under it has a relative positioning. just set the positioning to unset and you get the correct coordinates.
in my case i had to change following classes:
.modal-dialog {
position: unset;
}
.modal-content {
position: unset;
}
.modal-body {
position: unset;
}
.alert {
position: unset;
}
If you want you can do css stuff like "just change the position of modal-body inside modal dialog" so you dont mess up anything else :)
Related
I'm actually working on a project which overlay two image, with one that the user upload it and the second is by default.
My only problem is when the uploaded image is a rectangle and not a square, the canvas is resizing it. I need that the canvas crop my image, and be a square. Then I can apply the filter.
Here is my code:
$('.file1, .file2').on('change', function() {
var reader = new FileReader(),
imageSelector = $(this).data('image-selector');
if (this.files && this.files[0]) {
reader.onload = function(e) {
imageIsLoaded(e, imageSelector)
};
reader.readAsDataURL(this.files[0]);
}
});
$('.btn-merge').on('click', merge);
function imageIsLoaded(e, imageSelector) {
$(imageSelector).attr('src', e.target.result);
$(imageSelector).removeClass('hidden');
};
function merge() {
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
imageObj1 = new Image(),
imageObj2 = new Image();
canvas.width = 300;
canvas.height = 300;
imageObj1.src = $('.image1').attr('src');
imageObj1.onload = function() {
ctx.globalAlpha = 1;
ctx.drawImage(imageObj1, 0, 0, 300, 300);
imageObj2.src = $('.image2').attr('src');
imageObj2.onload = function() {
ctx.globalAlpha = 1;
ctx.drawImage(imageObj2, 0, 0, 300, 300);
var img = canvas.toDataURL('img/png');
$('.merged-image').attr('src', img);
var mergedimage = document.getElementById('mergedimage');
$('.merged-image').removeClass('hidden');
$("#downloadfinal").attr("href", mergedimage.src);
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<div class="container-fluid shadow">
<div class="container d-flex flex-column justify-content-center">
<div class="row mb-1 ">
<h2>Votre photo ici : </h2>
<label class="custom ml-3 hover-underline-animation"> <input class="ml-2 file1" type="file" data-image-selector=".image1" />Selectionner un fichier</label>
</div>
<img class="image1 hidden mb-4" alt="abs" width="200px" height="auto" />
<div class="row">
<h2 class="mr-2 ">Filtre par défaut : </h2>
<img alt="abs image3" width="200px" height="auto" src="img/filtre.png" />
</div>
<div class="otherfilter">
<div class="row mt-5 mb-5">
<h2 class="">Mettez un autre filtre ici : </h2>
<label class="custom ml-3 hover-underline-animation"> <input class="ml-2 file2" type="file" data-image-selector=".image2" />Selectionner un fichier</label>
</div>
<img class="hidden image2" alt="ab" width="200px" height="auto" src="img/filtre.png">
</div>
</div>
<br />
<div class="text-center mb-5">
<input class="btn-merge mb-3" type="button" value="Appliquer le filtre" />
<br />
<img class="merged-image hidden mb-3" id="mergedimage" alt="merged image" />
<canvas id="canvas" class="hidden"></canvas>
<br>
<a class="btn-dl" id="downloadfinal" role="button" href="#" download="photo_profil_modifie">
<i class="mr-2 bi bi-download"></i>Télécharger
</a>
</div>
Thanks for read this, hope my english is good, if you don't understand tell me.
To do this you need to scale the input image to fit the canvas either horizontally or vertically while maintaining it's aspect ratio.
That's not hard to do as things are a bit easier since the canvas is squarish. Say we have a canvas of 300 x 300 pixel and we want to draw a non-square image of 400 x 300 onto. First we take the width or height of the canvas - it does not really matter as it's the same - and divide it by the bigger side of the image - 400 in this case.
300 / 400 == 0.75
This is the scale we need to multiply both the width and height of the image before drawing it onto the canvas.
Here's an example:
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let imageObj1 = new Image();
imageObj1.crossOrigin = 'anonymous';
imageObj1.crossOrigin = 'anonymous';
imageObj1.onload = () => {
let scale = imageObj1.width > imageObj1.height ? canvas.width / imageObj1.width : canvas.height / imageObj1.height;
context.drawImage(imageObj1, canvas.width / 2 - imageObj1.width * scale / 2, canvas.height / 2 - imageObj1.height * scale / 2, imageObj1.width * scale, imageObj1.height * scale);
}
imageObj1.src = 'https://api.codetabs.com/v1/proxy?quest=https://picsum.photos/id/237/300/400';
#canvas {
background: blue;
}
<canvas id="canvas" width="300" height="300"></canvas>
If you prefer to not have unused area on your canvas and stretch the image to the whole canvas and crop the exceess, you simply divide the smaller side of your image by the canvas width/height.
For example:
//ctx.drawImage(imageObj1, 0, 0, 300, 300);let image = new Image();
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let imageObj1 = new Image();
imageObj1.crossOrigin = 'anonymous';
imageObj1.crossOrigin = 'anonymous';
imageObj1.onload = () => {
let scale = imageObj1.width < imageObj1.height ? canvas.width / imageObj1.width : canvas.height / imageObj1.height;
context.drawImage(imageObj1, canvas.width / 2 - imageObj1.width * scale / 2, canvas.height / 2 - imageObj1.height * scale / 2, imageObj1.width * scale, imageObj1.height * scale);
}
imageObj1.src = 'https://api.codetabs.com/v1/proxy?quest=https://picsum.photos/id/237/300/400';
<canvas id="canvas" width="300" height="300"></canvas>
I'm working on a proyect and i need to draw in almost 5 canvas. I've created the first canvas successfully but when i put in the code the second i only can draw on this latest canvas.
I read than the problem can be the context("2d") i tryed to separate saving in a different ctx varaible like, ctx2 or things like this.
This is my code:
HTML
<!--First Canvas-->
<div class="col-sm-12 col-lg-6 mt-5 d-flex flex-column justify-content-center">
<div id="contenedor-pizarra-cervical" class="contenedor-pizarra mx-auto">
<canvas id="pizarra-cervical"></canvas>
</div>
</div>
<!--Second Canvas-->
<div class="col-sm-12 col-lg-6 mt-5 d-flex flex-column justify-content-center">
<div id="contenedor-pizarra-postural" class="contenedor-pizarra mx-auto">
<canvas id="pizarra-postural"></canvas>
</div>
</div>
JS OF THE FIRST CANVAS WORKING OK:
//Canvas Cervical
var canvasCervical = document.getElementById("pizarra-cervical");
var ctx = canvasCervical.getContext("2d");
var painting = document.getElementById("contenedor-pizarra-cervical");
var paintStyle = getComputedStyle(painting);
canvasCervical.width = parseInt(paintStyle.getPropertyValue("width"));
canvasCervical.height = parseInt(paintStyle.getPropertyValue("height"));
var mouse = {x: 0, y: 0};
canvasCervical.addEventListener('mousemove', function(e){
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop
}, false);
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = '#7baeb0';
canvasCervical.addEventListener('mousedown', function(e){
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
canvasCervical.addEventListener('mousemove', onPaint, false);
}, false);
canvasCervical.addEventListener('mouseup', function(){
canvasCervical.removeEventListener('mousemove', onPaint, false)
},false);
var onPaint = function (){
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
};
If you require the same behaviour to happen on multiple canvasses, then you could define the configuration logic in its own function, providing the canvas element as an argument, like this:
let configureCanvas = canvas => {
let painting = canvas.parentNode;
let paintStyle = getComputedStyle(painting);
let mouse = { x: 0, y: 0 };
let onPaint = () => {
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
};
canvas.width = parseInt(paintStyle.getPropertyValue("width"));
canvas.height = parseInt(paintStyle.getPropertyValue("height"));
let ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = '#7baeb0';
canvas.addEventListener('mousemove', function(e) {
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop
}, false);
canvas.addEventListener('mousedown', function(e) {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false)
}, false);
canvas.nextElementSibling.addEventListener('click', function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
}
configureCanvas(document.getElementById("pizarra-cervical"));
configureCanvas(document.getElementById("pizarra-postural"));
canvas {
border: 1px solid #CCC;
}
<!--First Canvas-->
<div class="col-sm-12 col-lg-6 mt-5 d-flex flex-column justify-content-center">
<div id="contenedor-pizarra-cervical" class="contenedor-pizarra mx-auto">
<canvas id="pizarra-cervical"></canvas>
<button class="clear">Clear</button>
</div>
</div>
<!--Second Canvas-->
<div class="col-sm-12 col-lg-6 mt-5 d-flex flex-column justify-content-center">
<div id="contenedor-pizarra-postural" class="contenedor-pizarra mx-auto">
<canvas id="pizarra-postural"></canvas>
<button class="clear">Clear</button>
</div>
</div>
Taking this a step further, I'd suggest defining your own Class to handle the initialisation, configuration and event handlers of your canvas elements, but that's well outside the remit of this question.
I created a map where you can book bikes via Javascript.
The user is supposed to :
1) select a bike station (green station = bikes are available)
2) click on a button (reserver button)
3) sign in a canvas (in a modal)
The page is here :
http://p4547.phpnet.org/bikes/reservation.html
In my javascript, the Class Object is called this way :
document.addEventListener("DOMContentLoaded", event => {
new Signature();
This code is working fine if the canvas is located in the body of the page.
But the code is not working if the canvas is located in the modal.
I tried to code this way :
$('#bookingmodal').on('shown.bs.modal',function(event){
new Signature();
});
My modal ID is : #bookingmodal
Your problem is in the calculation of the coordinates for the mouse-position inside the canvas. If you resize the page to a really small window the drawing sometimes works (with an ugly offset).
I took your Signature-class and replaced the calculation for the mouse-position inside the canvas with a function that calculates the correct position of the mouse and also handles possible scaling of the bitmap used by the canvas:
updateMousePosition(mX, mY) {
let rect = this.canvas.getBoundingClientRect();
let scaleX = this.canvas.width / rect.width;
let scaleY = this.canvas.height / rect.height;
this.cursorX = (mX - rect.left) * scaleX;
this.cursorY = (mY - rect.top) * scaleY;
}
Example:
class Signature {
constructor() {
this.color = "#000000";
this.sign = false;
this.begin_sign = false;
this.width_line = 5;
this.canvas = document.getElementById('canvas');
this.offsetLeft = this.canvas.offsetLeft;
this.offsetTop = this.canvas.offsetTop;
this.context = canvas.getContext('2d');
this.context.lineJoin = 'round';
this.context.lineCap = 'round';
this.whenMouseDown();
this.whenMouseUp();
this.whenMouseMove();
this.createSignature();
this.clearCanvas();
this.resetCanvas();
}
updateMousePosition(mX, mY) {
let rect = this.canvas.getBoundingClientRect();
let scaleX = this.canvas.width / rect.width;
let scaleY = this.canvas.height / rect.height;
this.cursorX = (mX - rect.left) * scaleX;
this.cursorY = (mY - rect.top) * scaleY;
}
whenMouseDown() {
document.addEventListener("mousedown", ({
pageX,
pageY
}) => {
this.sign = true;
this.updateMousePosition(pageX, pageY);
})
}
whenMouseUp() {
document.addEventListener("mouseup", () => {
this.sign = false;
this.begin_sign = false;
})
}
whenMouseMove() {
this.canvas.addEventListener('mousemove', ({
pageX,
pageY
}) => {
if (this.sign) {
this.updateMousePosition(pageX, pageY);
this.createSignature();
}
})
}
createSignature() {
if (!this.begin_sign) {
this.context.beginPath();
this.context.moveTo(this.cursorX, this.cursorY);
this.begin_sign = true;
} else {
this.context.lineTo(this.cursorX, this.cursorY);
this.context.strokeStyle = this.color;
this.context.lineWidth = this.width_line;
this.context.stroke();
}
}
clearCanvas() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
resetCanvas() {
document.getElementById("reset").addEventListener("click", () => {
this.clearCanvas();
})
}
}
document.addEventListener("DOMContentLoaded", event => {
new Signature();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<button type="button" class="btn btn-success" data-target="#bookingmodal" data-toggle="modal">Réserver</button>
<div aria-labelledby="exampleModalLongTitle" class="modal fade" id="bookingmodal" role="dialog" tabindex="-1" style="display: none;" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Réservation</h5><button aria-label="Close" class="close" data-dismiss="modal" type="button"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<div class="guide">
<div class="row item">
<div class="col-md-12 order-md-2">
<h2 class="item-heading">Signature. <span class="text-muted">Signez pour valider votre réservation.</span></h2>
<canvas id="canvas" width="250" height="250">
</canvas>
<form>
<input type="button" id="reset" value="Réinitialiser" class="btn btn-danger">
</form>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-dismiss="modal" type="button">Fermer</button>
</div>
</div>
</div>
</div>
In my Angular project, I am playing with CSS to move the image/video element inside a "stage" element.
What I have so far:
SCSS:
.stage {
width: 400px;
height: 300px;
position: relative;
overflow: hidden;
background-color: coral
}
video {
position: absolute;
width: 500px;
height: 400px;
}
HTML:
<div class="stage">
<video controls autoplay [ngStyle]="{'top': top, 'left': left">
<source src="http://www.archive.org/download/AnimatedMechanicalArtPiecesAtMit/P1120973_512kb.mp4" type="video/mp4">
</video>
</div>
<div class="row">
<div class="col-1 ">
<button type="button" class="btn btn-primary btn-tn " (click)="moveLeft()">
Left
</button>
</div>
<div class="col-1">
<button type="button" class="btn btn-primary btn-tn " (click)="moveRight()">
Right
</button>
</div>
<div class="col-1">
<button type="button" class="btn btn-primary btn-tn " (click)="moveUp()">
Up
</button>
</div>
<div class="col-1">
<button type="button" class="btn btn-primary btn-tn " (click)="moveDown()">
Down
</button>
</div>
</div>
CODE:
l: number = 0;
t: number = 0;
left = '0px';
top = '0px';
moveUp() {
this.t -= 5;
this.top = this.t + 'px';
}
moveDown() {
this.t += 5;
this.top = this.t + 'px';
}
moveLeft() {
this.l -= 5;
this.left = this.l + 'px';
}
moveRight() {
this.l += 5;
this.left = this.l + 'px';
}
Result: Image/video moved and left blank space.
My question: how to keep image/video remain within the stage when moving left, right, up, down
For example, The image (blue) will be held when the image reaches to the top and left edge of stage (yellow).
Hope my explanation is clear. Any suggestion is appreciated.
I'm trying to create two animated boxes that appear on a button click, similar to the one when "About Me" is clicked on http://riccardozanutta.com/ . I wasn't able to figure how he did it, so I decided to go about it using modals. However, I was having trouble with getting both boxes to animate at the same time when I used two separate ones. I am able to get it to work when I have one modal with two different divs such as :
<div id="myModal" class="modal col-xs-12 col-lg-6">
<!-- Modal content -->
<div class="modal-content1">
<span class="close">×</span>
<h2>About Me</h2>
<p>blah blah>
</div>
<div class="modal-content2 col-xs-12 col-lg-6">
<span class="close">×</span>
<h2>Super powers</h2>
<p>blah blah>
</div>
</div>
However it is just one animation, and they are not joined together as in the one I'd like (with it also having them entering in from separate areas). Are modals what I need to do this, or is there a better way? While I was trying to find examples, I didn't find any that had two boxes. I'd appreciate any pointers in the right direction. Thank you in advance!
You can work on this and try to get it as close to your vision as possible. I just forked it, and edited a little to provide you an example and a place to work and find your perfect solution. Good luck!
Here's the example code:
JS
var test = document.getElementById('test');
var test1 = document.getElementById('test1');
function translate(elem, x, y) {
var left = parseInt(css(elem, 'left'), 10),
top = parseInt(css(elem, 'top'), 10),
dx = left - x,
dy = top - y,
i = 1,
count = 20,
delay = 20;
function loop() {
if (i >= count) {
return;
}
i += 1;
elem.style.left = (left - (dx * i / count)).toFixed(0) + 'px';
elem.style.top = (top - (dy * i / count)).toFixed(0) + 'px';
setTimeout(loop, delay);
}
loop();
}
function css(element, property) {
return window.getComputedStyle(element, null).getPropertyValue(property);
}
window.onclick = function(e) {
var arr;
if (e.target.nodeName === 'BUTTON') {
test.style.cssText = 'visibility:visible;';
translate(test, +50, +50);
test1.style.cssText = 'visibility:visible;';
translate(test1, +200, +50);
}
};
HTML
<button title="100 100">Translate to (100, 100)</button>
<!-- Modal content -->
<div id="test">
<span class="close">×</span>
<h2>About Me</h2>
<p>blah blah>
</div>
<div id="test1">
<span class="close">×</span>
<h2>Super powers</h2>
<p>blah blah>
</div>
CSS
#test {
position: absolute;
left: 50px;
top: 500px;
visibility: hidden;
}
#test1 {
position: absolute;
left: 100px;
top: -500px;
visibility: hidden;
}