html canvas position woe - javascript

I try to overlay a canvas exactly on top of a 256px by 256px image (of 8x8 grids) and draw an 8x8 black square --
var canvas = document.querySelector("canvas");
canvas.width = 1024;
canvas.height = 1024;
var c = canvas.getContext("2d");
c.fillRect(0, 0, 8, 8);
#wrapper {
margin: 0 auto;
width: 256px;
}
#img {
position: relative;
top: 0;
left: 0;
}
#canvas {
position: relative;
margin-top: 0;
margin-left: 0;
top: -256px;
left: 0;
z-index: 1;
}
<html>
<body>
<div id="wrapper">
<img id="img" src="https://i.imgur.com/0qjIa89.png" />
<canvas id="canvas"></canvas>
</div>
</body>
</html>
However, I don't understand why the black square drawn is slightly offset down relative to the grid image --
complete jsfiddle https://jsfiddle.net/ze8ufqcv/7/
How to position the canvas drawing exactly on top of the image?

The accepted answer is not shifting the image a few pixels down, but actually eliminating the gap between the initial position of the two elements. You can see this by leaving out the top: -256px; and you'll find a few pixels space in between the image and your canvas. This is eliminated by adding the display:block; .
An alternative would be setting the wrapper to position:relative; and the canvas to position:absolute; and deleting the top offset.

This issue is due to the img having a vertical-align: baseline which causes 3px space to be added at the bottom.
You can remove this by adding vertical align: middle to the img.
Since vertical-align doesn't apply to block-level elements, display:block does the job as well.
var canvas = document.querySelector("canvas");
canvas.width = 1024;
canvas.height = 1024;
var c = canvas.getContext("2d");
c.fillRect(0, 0, 8, 8);
#wrapper {
margin: 0 auto;
width: 256px;
}
#img {
position: relative;
vertical-align: middle;
/*or display: block*/
/* not necessary
top: 0;
left: 0; */
}
#canvas {
position: relative;
margin-top: 0;
margin-left: 0;
top: -256px;
left: 0;
z-index: 1;
}
<body>
<div id="wrapper">
<img id="img" src="https://i.imgur.com/0qjIa89.png" />
<canvas id="canvas"></canvas>
</div>
</body>

Add display: block; to your #img selector.
Like this .
#img {
position: relative;
top: 0;
left: 0;
display: block;
}

Related

How to Swap Two Divs With Animation

I have a project where I want a div to appear as a large box and three more to appear underneath as smaller boxes and when you click a smaller box, it switches sizes and places with the large box using css transitions to make the movement and size change smooth. Right now I'm attempting to use jQuery and the positioning is not working at all. Here's an example of what I have so far:
https://jsfiddle.net/v3pmhawj/1/
$(function () {
let { left: x1, top: y1 } = $('.full-size-card').offset()
$('.inactive-sheets .card').on('click', function() {
let { left: x2, top: y2 } = $(this).offset()
let curr = $('.full-size-card')
let diffX = x2 - x1
let diffY = y2 - y1
$(this).css({
left: -diffX,
top: -diffY
})
$(this).addClass('full-size-card')
curr.css({
left: diffX,
top: diffY
})
curr.removeClass('full-size-card')
})
})
If anyone has suggestions on ways that involve other libraries or other techniques, I'm all ears. I'd like to be able to move the divs around in the DOM as well but as far as I can tell, you can't css-transition them if you do that since the only way (I know of) is to delete and re-add a copy of the element where you want it in the DOM.
You can create animation effect using transitions only. To achieve this you will have to define width and height of your containers as well as top and left position of bottom elements.
On click, you just have to exchange classes of element that will become small and of element that will become large.
Here is fiddle of an example:
https://jsfiddle.net/fkd3ybwx/210/
HTML
<div class="card-container">
<div class="card large">A</div>
<div class="card small">B</div>
<div class="card small">C</div>
<div class="card small">D</div>
</div>
CSS
.card-container {
position: relative;
}
.card {
transition: all ease 1s;
position: absolute;
font-size: 24px;
border: white 4px solid;
box-sizing: border-box;
cursor: pointer;
}
.small {
width: 100px;
height: 100px;
background: blue;
left: 0;
top: 300px;
}
.small ~ .small {
left: 100px;
background: green;
}
.small ~ .small ~ .small {
left: 200px;
background: yellow;
}
.large {
width: 300px;
height: 300px;
background: red;
left: 0px;
top: 0px;
}
JavaScript
const smallCards = document.querySelectorAll('.card');
smallCards.forEach((smallCard) => {
smallCard.addEventListener('click', (event) => {
const largeCard = document.querySelector('.large');
largeCard.className = "card small";
event.target.className = "card large";
});
});

How to absolutely position shape elements relative to an underlying image?

The page has a centered image -- a map -- and I need to figure out how to mark points of interest on that map with small dots.
My plan is to draw the dots with very small circle elements, but how can I position them so that they will sit in the same place on the map every time the webpage is loaded on different sized screens? I would just photoshop the dots onto the image if I could, but I will need to write the javascript to have the dots be interactive (show a text box description on mouseover) so that won't work.
<!DOCTYPE html>
<html>
<head> <meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="example.css" />
</head>
<body>
<img src="example.jpg" style="margin-left:auto;margin-right:auto;"></img>
</body>
Wrap the image in a div, give it position:relative and the position the points (divs) absolutely using % values.
Here's an example I keep around:
Codepen Demo
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.map {
margin: 10px;
border: 5px solid red;
position: relative;
display: inline-block;
}
.map img {
max-width: 100%;
display: block;
}
.box {
width: 8%;
height: 8%;
background-image: url(http://www.clker.com/cliparts/W/0/g/a/W/E/map-pin-red.svg);
background-position: top center;
background-repeat: no-repeat;
background-size: contain;
position: absolute;
}
#pin-1 {
top: 25%;
left: 36%;
}
.box:hover>.pin-text {
display: block;
}
.pin-text {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 75%;
white-space: nowrap;
display: none;
}
.pin-text h3 {
color: white;
text-shadow: 1px 1px 1px #000;
}
<div class="map">
<img src="https://www.homesinc.com/wp-content/uploads/2017/05/1.jpg" alt="" />
<div id="pin-1" class="box">
<div class="pin-text">
<h3>My House</h3>
</div>
</div>
</div>
in case your points are dynamic and you can not set them in css, you could use canvas. this is a static example, it can be converted to dynamic if needed, could be considerably more work than positionning in percentages with css so if you know your point of interest positions you should go with CSS, if they are dynamic canvas is a good option
Codepen Demo
code bellow...
// You will need the background of the map and an array of points of interest
// containing x and y coordinates relative to the map
const mapImageUrl = 'http://via.placeholder.com/500x300'
const pointsOfInterest = [
{name:'point1', x:420, y:50},
{name:'point2', x:50, y:134},
{name:'point3', x:100, y:200}
]
// get refference to the canvas and to its context
const canvas = document.getElementById('map')
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
// create a new image element that would hold your background
var mapImg = new Image();
// this block executes when the image is loaded
mapImg.onload = function () {
//setting the canvas size to the image size
canvas.width = mapImg.width;
canvas.height = mapImg.height;
//drawing the image to the canvas
ctx.drawImage(mapImg, 0, 0);
//for each point draw a red dot positioned to pointsOfInterest[i].x, pointsOfInterest[i].y
//here you could alose use the point of interest name or whatever you have availible on your json
for(let i = 0; i < pointsOfInterest.length; i ++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(pointsOfInterest[i].x, pointsOfInterest[i].y,15,0,2*Math.PI);
ctx.stroke();
ctx.fill();
}
};
// set the url of the image, once loaded it will trigger image.onload
mapImg.src = mapImageUrl;
html, body {
height: 100%;
}
.mapContainer {
display: flex;
align-items: middle;
justify-content: center;
margin: 0;
height: 100%;
flex-wrap: wrap;
}
#map{
align-self: center
}
<div class="mapContainer">
<canvas id="map"> </canvas>
</div>

Center an image and scale height/width to fit inside flexbox parent?

I'm trying to center and scale an image inside a container. In the following diagrams, the pink box is a 16:9 container and the blue box is the image.
If the image is wider than the container, it will scale to fit.
If the image is taller than the container, it will also scale to fit.
If the image fits in the container, it will simply be centered.
As you can see in the diagrams, there is also a caption div aligned to the bottom left of the image, and a close icon aligned to the top right.
Here is the code I have now:
/* The page */
.container {
width: 100%;
height: 100%;
}
/* 16:9 container */
.imageWrapper {
padding-bottom: 56.25%;
position: relative;
width: 100%;
border: 1px solid red;
}
.imageInnerWrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* The image, footer and close button all need to fit inside the container */
.imageAndFooterWrapper {
display: inline-flex;
flex-direction: column;
position: relative;
border: 1px solid lightblue;
}
.image {
max-height: 100%;
object-fit: contain;
}
.footer {
text-align: left;
}
.closeButton {
position: absolute;
top: -30px;
right: -30px;
}
<div class="container">
<div class="imageWrapper">
<div class="imageInnerWrapper">
<div class="imageAndFooterWrapper">
<img class="image" src="https://images.theconversation.com/files/204986/original/file-20180206-14104-1hyhea9.jpg?ixlib=rb-1.1.0&rect=0%2C1212%2C5550%2C2775&q=45&auto=format&w=1356&h=668&fit=crop">
<div class="footer">
Caption
</div>
<div class="closeButton">X</div>
</div>
</div>
</div>
</div>
The following CodePen contains the above code, with some examples of different sized images.
https://codepen.io/anon/pen/NMKxxm
This may not be a direct answer to your question but usually when I have to do something like this, I use a div with a background image instead of an img tag. Using a div with a bg image allows you to use styles like background-image, background-position and background-size which allow you to create the effect as described by you.
Sample:
var imgDiv = $('.image')[0];
var closeButton = $('.fixed-el')[0];
var img = document.createElement('img');
img.src = getComputedStyle(imgDiv).backgroundImage.split('"')[1];
var calculate_positions = {
img_width: img.naturalWidth,
img_height: img.naturalHeight,
img_ratio: function() {
return calculate_positions.img_width / calculate_positions.img_height;
},
elm_ratio: function(elm) {
return $(elm).width() / $(elm).height();
},
img_offset: function(elm) {
var offset = []; //[x,y]
if (calculate_positions.elm_ratio(elm) > calculate_positions.img_ratio()) {
//centered x height 100%
var scale_percent = $(elm).height() / calculate_positions.img_height;
var scaled_width = calculate_positions.img_width * scale_percent;
var x_offset = ($(elm).width() - scaled_width) / 2;
offset = [x_offset, 0];
} else {
//centered y width 100%
var scale_percent = $(elm).width() / calculate_positions.img_width;
var scaled_height = calculate_positions.img_height * scale_percent;
var y_offset = ($(elm).height() - scaled_height) / 2;
offset = [0, y_offset];
}
return offset;
}
}
function updatePosition() {
var offset = calculate_positions.img_offset($('div.image'));
closeButton.style.top = offset[1] + 'px';
closeButton.style.left = offset[0] + 'px';
}
$(window).resize(updatePosition)
$(img).load(function() {
updatePosition();
});
div.image {
width: 100%;
background-image: url('http://via.placeholder.com/100x100');
background-position: center;
background-size: contain;
background-repeat: no-repeat;
flex: 1;
}
html,
body,
div.container {
width: 100%;
height: 100%
}
div.container {
display: flex;
position: relative;
flex-direction: column;
}
div.fixed-el {
position: absolute;
width: 20px;
height: 20px;
background: red;
}
div.caption {
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="image"></div>
<div class="caption">Some caption here</div>
<div class="fixed-el"></div>
</div>
EDIT:
You can change the image size in the styles and resize the window to see the scaling in action.
I also noticed the comment which mentioned that you do not want to use background image as it will clip the image. This will not happen if you use background-size:contain
EDIT 2:
It turns out you can actually figure out what the coordinates of the image are and position other elements around it. Have created a dirty hack to demonstrate this (mixed jQuery and vanilla JS, no proper scoping etc).. But you should be able to get the main idea and implement a neater solution. The main idea is derived from this question on SO

How to centre a <div> within several other <div>s (using a mixture of w3.css and css)

I am using w3-css and bog-standard css.
I have a png of a word: INSPIRE, whose lettering is transparent. Behind the letters are coloured rectangles. In front of the coloured rectangles are white rectangles that give the impression of the INSPIRE letters being filled up, depending on how high the rectangles reach. The example (under the link) has the white rectangles set to 50% height. In the original, these heights are determined by numbers pulled from a database and not set the way they are in the script.
The filling of the INSPIRE happens thus:
There is a 'w3 div' called w3-third, within this sits another div called w3-card, within this is an ordinary div - styled in the header - called back and within this sits the divs for each letter block and finally the div for the INSPIRE png. I make use of z values so the png sits on top.
I cannot for the life of me get the INSPIRE image to sit centrally within the w3-card div.
If I could wave a magic wand then I would 'center' the #back div. I've tried setting a width...
#back {
position: absolute;
width: 294px;
margin: 0 auto;
z-index: 0;
}
But it makes no difference.
Please, please help before my head explodes...
Below is the full page code for the link:
<html>
<head>
<title>
INSPIRE
</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://www.w3schools.com/lib/w3.css">
<link rel="stylesheet" href="http://www.w3schools.com/lib/w3-theme-black.css">
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css">
<style>
#back {
position: absolute;
width: 294px;
margin: 0 auto;
z-index: 0;
}
#behindI {
position: absolute;
left: 0px;
z-index: 1;
}
#behindN {
position: absolute;
left: 33px;
z-index: 1;
}
#behindS {
position: absolute;
left: 80px;
z-index: 1;
}
#behindP {
position: absolute;
left: 113px;
z-index: 1;
}
#behindII {
position: absolute;
left: 160px;
z-index: 1;
}
#behindR {
position: absolute;
left: 200px;
z-index: 1;
}
#behindE {
position: absolute;
left: 247px;
z-index: 1;
}
#front {
position: absolute;
z-index: 3;
}
#myCanvasI {
z-index: 2;
}
</style>
<script>
var i = 0.5;
var n = 0.5;
var s = 0.5;
var p = 0.5;
var ii = 0.5;
var r = 0.5;
var e = 0.5;
</script>
</head>
<body>
<div class="w3-third">
<div class="w3-card-2 w3-padding-top w3-black w3-center" style="min-height:460px">
<h3>Characteristics of Learning</h3><br>
<p>How are you doing in each area? </p>
<p>Are you the full INSPIRE? </p>
<div id="back">
<div id="front">
<img src="images/fullinspire33.png" />
</div>
<div id="behindI">
<canvas id="myCanvasI" width="33" height="33">
<script>
var z=document.getElementById("myCanvasI");
var ctx=z.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(0,0,33,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,33,33*i);
ctx.stroke();
</script>
</canvas></div>
<div id="behindN">
<canvas id="myCanvasN" width="47" height="33">
<script>
var y=document.getElementById("myCanvasN");
var ctx=y.getContext("2d");
ctx.fillStyle="orange";
ctx.fillRect(0,0,47,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,47,33*n);
ctx.stroke();
</script>
</canvas> </div>
<div id="behindS">
<canvas id="myCanvasS" width="33" height="33">
<script>
var x=document.getElementById("myCanvasS");
var ctx=x.getContext("2d");
ctx.fillStyle="yellow";
ctx.fillRect(0,0,33,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,100,33*s);
ctx.stroke();
</script></canvas></div>
<div id="behindP">
<canvas id="myCanvasP" width="47" height="33">
<script>
var w=document.getElementById("myCanvasP");
var ctx=w.getContext("2d");
ctx.fillStyle="green";
ctx.fillRect(0,0,47,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,47,33*p);
ctx.stroke();
</script>
</canvas>
</div>
<div id="behindII">
<canvas id="myCanvasII" width="40" height="33">
<script>
var v=document.getElementById("myCanvasII");
var ctx=v.getContext("2d");
ctx.fillStyle="blue";
ctx.fillRect(0,0,40,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,40,33*ii);
ctx.stroke();
</script>
</canvas>
</div>
<div id="behindR">
<canvas id="myCanvasR" width="47" height="33">
<script>
var u=document.getElementById("myCanvasR");
var ctx=u.getContext("2d");
ctx.fillStyle="indigo";
ctx.fillRect(0,0,47,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,47,33*r);
ctx.stroke();
</script>
</canvas>
</div>
<div id="behindE">
<canvas id="myCanvasE" width="47" height="33">
<script>
var t=document.getElementById("myCanvasE");
var ctx=t.getContext("2d");
ctx.fillStyle="violet";
ctx.fillRect(0,0,47,33);
ctx.stroke();
ctx.fillStyle="white";
ctx.fillRect(0,0,47,33*e);
ctx.stroke();
</script>
</canvas>
</div>
</div>
</div>
</div>
</body>
</html>
Just add another div that wraps the div with id black like this
<div style="
display: inline-block;
width: 295;
height: 35px;
margin: auto;
">
<div id="back"> you stuff in this div </div>
</div>
Just in case you want to keep your div with id black as position:absolute;
You cant use position: absolute; for margin 0 auto you need to use position relative or static to do this
Add a position:relative to the w3-card div.
After that, all the child elements which are on position:absolute; will get positionned related to that element.
Appending left:0px; right:0px; to #back will do the thing then.
The changes will be:
#back {
position: absolute;
width: 294px;
margin: 0 auto;
z-index: 0;
left: 0px;
right: 0px;
}
and
#w3-card {
position: relative;
}
Add this to the css:
.w3-card-2{ position: relative; }
#back{ left: 50%; margin-left: -146px; }
The -146px are half of the element with, so if set dynamically, take it into account
This puts the element 50% to the left of the container (that needs to be positioned, thus position relative in parent). And then moves it half its width to the left so its it's center that is positioned in the center and not its left side.
As Johannes say, you could try wrapping #front in a relatively positioned div and setting margin: 0 auto.
Or you can set:
.w3-card-2 {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
You'll have to set the children element's 'order' property in CSS to get them back in the same order as before, but that's easy.

Javascript/Jquery - Increase padding percentage of div on click

I have a problem that I've spent the better part of half a day trying to solve with no real solution I'm happy with. I have a carousel which contains a placeholder image for a youtube video, and when you click on it the placeholder image is removed and is replaced by the youtube video, but as the aspect ratio of the placeholder image is different from the video, and I need this to work responsively, I need to increase the percentage of the padding of the div these sit in when the user clicks (or taps) on the div.
So, my HTML for this div looks as such:
<div class="youtube_video">
<img src="img/video_poster_carousel.jpg" width="986" height="308">
<!-- <iframe width="986" height="555" src="https://www.youtube.com/embed/Wt_Ruy_ejPY?enablejsapi=1&list=PL027E2B6D9900A88F&showinfo=0&controls=1" frameborder="0" allowfullscreen></iframe> -->
</div>
And the CSS:
/* video */
.youtube_video { position: relative; padding-bottom: 31.65%; height:0; }
.youtube_video img { position: absolute; display: block; top: 0; left: 0; /*width: 100%; height: 100%;*/ z-index: 20; cursor: pointer; }
.youtube_video:after { content: ""; position: absolute; display: block;
background: url(play-button.png) no-repeat 0 0;
top: 45%; left: 45%; width: 46px; height: 36px; z-index: 30; cursor: pointer; }
.youtube_video iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/* image poster clicked, player class added using js */
.youtube_video.player img { display: none; }
.youtube_video.player:after { display: none; }
And the Javascript being used right now:
$(function() {
var videos = $(".youtube_video");
videos.on("click", function(){
var elm = $(this),
conts = elm.contents(),
le = conts.length,
ifr = null;
for(var i = 0; i<le; i++){
if(conts[i].nodeType == 8) ifr = conts[i].textContent;
}
elm.addClass("player").html(ifr);
elm.off("click");
});
});
Basically, I need to increase the padding-bottom on .youtube_video from 31.65% to 56.25% (16:9 ratio) upon clicking on the div. I'm probably missing something obvious, but Javascript/jQuery isn't my strongest point. Any help would be appreciated.
Try adding this to your click function?
$('.youtube_video').css('padding-bottom','56.25%');
JQuery CSS
DEMO
If you have the width of the <iframe> you can allways set its size and animate it with jquery.
$('.video-container').animate({
width:'200px',
height:'200px',
});

Categories

Resources