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>
Related
SOLVED (I think) -- See Below.
Quick Background: For a simple IOS game I have 16:9 background area that I am floating images over top of that need to blend into the background. For that, I need them to resize with the background and move with the background.
Hypothesis: I'm keeping the game area 16:9 because I assume that will scale well with various devices. If I also make the images that need to float on top of the background 16:9, they should scale at the same rate and provide a steady % scale to also move them in place.
Test: I have a background area of 1280x720, with a slice cut out 256x144(20%). I have an image of 256x144 that I'm trying to overlay that slice, which sometimes works, but doesn't always respond well to resizing or doesn't line up on various devices. Is my math somehow wrong?
Here's the code: JS Fiddle: Testing Image Overlap
SOLVED -
I was incorrectly assuming that the width of the floated image and the % it needed to be moved was based on a % of the window width (as is the case with the container DIV) to maintain it's 16:9 ratio, but the incorrect results occurred when the 16:9 ratio of the container maxed out at less than the screen width. What I actually want is the image nested into the container and use a % of that parent instead of the window! Can't believe I missed that for hours...
New Code (Hope this will help someone): Updated Fiddle - Proper Image Scaling
<style>
body {
margin: 0;
padding: 0;
}
#Game_Wrapper {
width: 100vw;
height: calc(100vw * 9 / 16);
margin: auto;
padding: 0;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
border: 1px solid red;
background: lightblue url('https://i.imgur.com/BFCIQuw.png') center/contain no-repeat;
}
#Game_Area {
margin: 0;
padding: 0;
width: 100%;
height: calc(100% - 60px);
}
#GUI_Top {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Top p {
margin: 0;
padding: 0;
text-align: center;
}
#GUI_Bottom {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Bottom p {
margin: 0;
padding: 0;
text-align: center;"
}
/* 100 * 16/9 = 177.778 */
#media (min-width: 177.778vh) {
#Game_Wrapper {
height: 100vh;
width: calc(100vh * 16 / 9);
}
}
.Building_Position {
width: calc(100vw * .2);
height: calc(100vh * .2);
margin: 0;
padding: 0;
text-align: center;
font-weight: bold;
border: 1px solid #000;
}
#Building1 {
margin: 0;
padding: 0;
float: left;
position: absolute;
left: calc(100vw * .40);
top: calc(100vh * .20);
}
#Building1 img {
margin: 0;
padding: 0;
width: calc(100vw * .20);
height: calc(100vh * .20);
}
<meta charset="utf-8" name="viewport" content= "width=device-width, initial-scale=1.0">
<title>Test Game Layout</title>
</head>
<body>
<div id="Game_Wrapper">
<!-- This will hold the entire game and GUI -->
<div id="GUI_Top">
<!-- This is where the top GUI will be -->
<p>This is the top GUI / NAV</p>
</div>
<div id="Game_Area">
<!-- This is where you can interact with the actual game -->
</div>
<div id="GUI_Bottom">
<!-- This is where the bottom GUI will be -->
<p>This is the bottom GUI / NAV</p>
</div>
<div class="Building_Position" id="Building1">
<a href="#Link To Function" alt="Building Name" title="Building Name & Level" class="load_view">
<img class="building_image" src="https://i.imgur.com/dqEFcAu.png">
</a>
</div>
</div>
</body>
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
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;
}
So, what I ultimately want to achieve is this:
https://s17.postimg.org/7zkpt511r/imgdivs.png
what I have so far:
<style type="text/css">
.wrapper{
height: 100%;
width: 100%;
}
.left{
float: left;
background: silver;
height: 100%;
}
.right{
background: silver;
float: right;
height: 100%;
}
canvas{
position: absolute;
height: 100%;
}
</style>
<div class = "wrapper">
<div class = "left"></div>
<canvas id="canvas"></canvas>
<div class = "right">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
var WIDTH = 200; //could be anything, comes from previous calculations
$('#canvas').css("width", WIDTH);
$('#canvas').css("left", (window.innerWidth - WIDTH) / 2);
$('.left').css("width", (window.innerWidth - WIDTH) / 2);
$('.right').css("width", (window.innerWidth - WIDTH) / 2);
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, WIDTH, window.innerHeight);
</script>
So a little explanation, the canvas in the middle is dynamic in it's width(calculated with javascript) and pre-decided with it's height(100%), it is placed in the middle of the page, and the two divs to it's sides should stretch accordingly and equally and fill the rest of the page. I have many problems in my code; the divs sometimes overflow one below another, the canvas for some reason doesn't fillStyle it's whole dimension and so on.
I am afraid to try to do it in flexbox since old browsers do not support it properly, and I need all browsers support. Thank you for your time.
To get the div height to work you need to add this to your css
html {
height: 100%;
}
body {
min-height: 100%;
}
Dont use position:absolute's it is not a good pratice for layouts
You can consider using display:flex for the same and consider using viewport width and height vw/vh
When you give your div width 100% and height 100% nothing gets effected to make it work you have to set html,body{width:100%,height:100%} which is not a good practice ,instead you can use vw/vh
check this snippet
.wrapper {
height: 100vh;
border: 1px solid black;
display: flex;
width: 100vw;
}
.left {
border-right: 1px solid;
background: silver;
height: 100%;
width: 20%;
}
.right {
background: silver;
width: 30%;
height: 100%;
border-left: 1px solid;
}
canvas {
width: 60%;
height: 100%;
}
<div class="wrapper">
<div class="left"></div>
<canvas id="canvas"></canvas>
<div class="right">
</div>
Solution without display:flex
.wrapper {
height: 100vh;
border: 1px solid black;
position: relative;
width: 100vw;
}
.left {
border-right: 1px solid;
background: silver;
height: 100%;
width: 20%;
position: absolute;
left: 0;
}
.right {
background: silver;
width: 30%;
height: 100%;
border-left: 1px solid;
float: right;
}
canvas {
width: 60%;
height: 100%;
}
<div class="wrapper">
<div class="left"></div>
<canvas id="canvas"></canvas>
<div class="right">
</div>
Hope it helps
CSS:
body {
margin:0;
height:100vh;
}
.wrapper{
display:table;
height: 100%;
width: 100%;
background-color:pink;
}
.left{
display:table-cell;
background-color: gray;
height: 100%;
}
.right{
background-color: blue;
display:table-cell;
height: 100%;
}
canvas{
display:table-cell;
height: 100%;
}
JS:
var WIDTH = 234; //could be anything, comes from previous calculations
$('#canvas').css("width", WIDTH+'px');
$('.left').css("width", (window.innerWidth - WIDTH) / 2+'px');
$('.right').css("width", (window.innerWidth - WIDTH) / 2+'px');
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
DEMO: https://jsfiddle.net/zd75wq2e/4/
So, basically, you was close. I've removed absolute positioning and floats, and i've rather used css table. I hope it has good support in older browsers, too. And, yes, as mentioned in comment, fill problem is solved in last line of js.
I've been trying in this jsfiddle and I get a working solution. I added a float:left; to the wrapper and I removed all the height: 100%; because they're ignored by the browser. I just set the height according to the window element using JS.
I removed as well the position: absolute; on the canvas because is not needed to do what you need. Anyway if you need it for other purposes, my suggestion is then insert the canvas inside a div class="center" (for example) and apply the position absolute to the canvas inside that div.
Finally I added as well a $(document).ready(function(){ ... }); to ensure the JS code is executed when the page is completely loaded.
EDIT: The divs calculation is made using width: calc(50% - canvasWidth/2);,this way it is working as well when you resize the window. But I thing the most important thing is working using WIDTH is producing your error, but getting the $("#canvas").outerWidth() is working fine...
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.