I have an image img inside a DIV modal, and a button + at the bottom right. This button calls the function imgfit() that toggles the image view as "fit and center inside the window" or "100%" (i.e., 1:1 pixel). It works, except for the fact that the scrollbars do not allow me to scroll the top/left of the image: the image is displaced negatively inside the div when seen at 1:1.
How do I tell the scrollbars that they "forgot" part of the image at the top/right? You can see the problem here (make your browser window relatively small to better see the problem).
The button calls the following function imgfit:
function imgfit()
{
if (fit) {
img.style.minWidth = "0px";
img.style.minHeight = "0px";
img.style.maxWidth = "100vw";
img.style.maxHeight = "100vh";
modal.style.overflow = "hidden"; // Prevent scroll of fullsize image
} else {
img.style.maxWidth = 'none';
img.style.maxHeight = 'none';
img.style.minWidth = img.naturalWidth+"px";
img.style.minHeight = img.naturalHeight+"px";
modal.style.overflow = "auto"; // Allow scroll of fullsize image
}
fit = ! fit;
}
This is the CSS of the image and the modal div (if I remove the align-items: center and the justify-content: center, problem is gone, but image is not centered):
.modal {
align-items: center;
justify-content: center;
position: fixed;
z-index: 100;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #000;
-moz-user-select: none;
user-select: none;
touch-action: manipulation;
}
.img {
cursor: no-drop;
position: absolute;
margin: auto;
touch-action: manipulation;
}
I solved the problem thanks to the neat article here (I didn't know that sitepoint.com site). The culprit was the position: absolute thing. Changing to display: grid solved the issue:
.modal {
display: grid;
align-items: center;
position: fixed;
z-index: 100;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #000;
-moz-user-select: none;
user-select: none;
touch-action: manipulation;
}
.img {
grid-column: 1;
grid-row: 1;
cursor: no-drop;
margin: auto;
touch-action: manipulation;
}
Example use (what I want)
div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}
Test case showing it NOT working:
CodePen: CSS Variables in Pseudo Element's "content:" Property (a test case) by Jase Smith
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
content: var(--mouse-x, 245)" / " var(--mouse-y, 327);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>
<!-- explanation (not test case) -->
<main>
<pre><code>div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
<h1>If this worked...</h1>
<p>
We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
</p>
</main>
Edit for clarity: CSS custom properties with integer values can be displayed in a pseudo-element's content property via a CSS counter.
div {
--variable: 123;
}
span:after {
counter-reset: variable var(--variable);
content: counter(variable);
}
<div>The variable is <span></span>.</div>
.coordinates:before {
counter-reset: x var(--x) y var(--y);
content: 'The coordinates are (' counter(x) ', ' counter(y) ').';
}
<div class="coordinates" style="--x: 1; --y: 2"></div>
Original Answer
Got it to work using a hack involving CSS Counters. Enjoy.
div::after {
counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
content: counter(mouse-x) " / " counter(mouse-y);
}
Full code in action:
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
content: counter(mouse-x) " / " counter(mouse-y);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>
<!-- explanation (not test case) -->
<main>
<pre><code>div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
<h1>If this worked...</h1>
<p>
We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
</p>
</main>
I'm not quite sure if I understood your question correctly, but I think here's a solution...
You can define a custom attribute to your <div> element.
<div data-position></div>
Then assign the position in this attribute with javascript:
var position = e.clientX + " " + e.clientY
document.querySelector("div").setAttribute('data-position', position)
Finally use the attr() CSS function in the content property of your pseudoelement.
div::after {
content: attr(data-position);
}
And voila.
Code Snippet:
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
var position = e.clientX + "/" + e.clientY
document.querySelector("div").setAttribute('data-position', position)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
content: attr(data-position);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<div data-position></div>
<span class="x"></span>/<span class="y"></span>
content property only allows Strings, and since you are dealign with numbers and CSS cannot cast variables, you are left with the option to create another set of variables (from JS) which will serve as the printed values, and will be of type String.
To set --mouse-x-text as String, it's not enough to cast it to that type using the old casting trick 2+"" = "2", but JSON.stringify is the only way what I know that can output a "real" string, out of the already-string value, which kind-of mean a string of a string, since CSS seems to strip the first string-layer.
document.addEventListener('mousemove', ({clientX:x, clientY:y}) => {
const {style} = document.documentElement
style.setProperty('--mouse-x', x)
style.setProperty('--mouse-y', y)
// for printing
style.setProperty('--mouse-x-text', JSON.stringify(x+""))
style.setProperty('--mouse-y-text', JSON.stringify(y+""))
})
body::before{
content: "X:"var(--mouse-x-text)" Y:"var(--mouse-y-text);
}
You need quotation marks around the values of your custom properties.
document.documentElement.style.setProperty('--mouse-x', "'" + e.clientX + "'")
document.documentElement.style.setProperty('--mouse-y', "'" + e.clientY + "'")
I am using the Tizen 2.3.1 SDK for wearables and have run into an issue where no matter what I do to the style.css file, adding any new ids simply refuses to work.
* {
margin: 0;
padding: 0
}
html, body {
width: 100%;
height: 100%;
background-color: black;
}
#str_day {
position: absolute;
display: -webkit-flex;
width: 50%;
height: 50%;
background: yellow;
color: yellow;
z-index: 4;
opacity: 1;
}
h1 {
font-size: 1.4em;
margin: 10px 0 20px 10px;
color: #6587ac;
}
#box {
display: -webkit-flex;
-webkit-align-items: center;
width: 100%;
height: 100%
}
.canvas {
background: #000;
width: 320px;
height: 320px;
margin: auto;
}
This is my code. I am trying to add the str_day id however it doesn't show up when I run the package through the emulator. The rest of the css (the canvas element, mainly) works fine, but str_day just doesn't show up. I tried specifying the z-index and opacity in case some weird issue was occuring but that doesn't seem to be the problem.
This is my full code on github.
https://github.com/JoyfulOwl/RadialWatch/commit/86d096710c7187b6b3679e02416548e18a3e986e
Probably because of improper CSS layout str_day is rendered/painted outside the viewport of your watchface because of which its not visible.
I think better would be to use canvas fillText command to print the date on canvas.
Try below example.
var canvas = document.getElementById("myCanvas");
var context=canvas.getContext("2d");
context.font="30px Comic Sans MS";
context.fillStyle = "red";
context.textAlign = "center";
context.fillText(str_Day, mCenterX, mCenterY); // mCenterX & mCenterY are the position where text has to be shown.
Container's DOM width after transform is the same as before transform?
Why?
var theScale = aNumber;
var containerWidth = theContainer.width();
alert(containerWidth);
// and the other prefixes, as well
theContainer.css("-webkit-transform", "scale(" + theScale + ")");
containerWidth = theContainer.width();
alert(containerWidth); // the same value ???
Transforms don't affect the layout — or more precisely the box model — of an element. They are purely cosmetic. From the spec:
Note: Transformations do affect the visual layout on the canvas, but have no affect on the CSS layout itself. This also means transforms do not affect results of the Element Interface Extensions getClientRects() and getBoundingClientRect(), which are specified in [CSSOM-VIEW].
I have solved the problem with ::before.
The tag has the right size and I scale the image in the ::before layer.
*, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.card {
width: calc(1178px / 2);
height: calc(1280px / 2);
position: relative;
border: solid 5px blue;
}
.card::before {
content: "";
position: absolute;
border: solid 5px red;
width: calc(100%);
height: calc(100%);
background-image: url(./seasons-of-the-year.png);
transform: scale(0.5);
transform-origin: left top;
}
The code below is attached to window.onresize = resize;. The baseWidth and baseHeight are read on load as a basis for the calculations. The main variable is defined just by setting it to the main html node. The font is set on a block element to cause all of the em based elements within it to resize in kind. When the width or height of the browser is changed then the ratio is recalculated. Please see demo to understand what I achieve with JS but would like to find a pure CSS solution: http://codepen.io/anon/pen/nLauF
I have been exploring options in CSS3 such as calc. Feel free to also suggest any improvements to the JS below also.
function resize() {
var height = 0,
width = 0;
if(window.innerWidth <= window.innerHeight) {
size = window.innerWidth / baseWidth;
height = baseHeight * size;
width = window.innerWidth;
} else {
size = window.innerHeight / baseHeight;
height = window.innerHeight;
width = baseWidth * size;
}
if(baseWidth * size > window.innerWidth) {
size = window.innerWidth / baseWidth;
height = baseHeight * size;
width = window.innerWidth;
}
main.style.height = height + "px";
main.style.width = width + "px";
main.style.fontSize = size * 16 + "px";
}
Thanks!
I wrote this code including font-size calculation with vmin units :
DEMO
CSS :
main {
width: 80vmin;
height: 60vmin;
background-color: #000;
position: absolute;
top:0; bottom:0;
left:0; right:0;
margin:auto;
}
h1 {
color: #fff;
font-size: 30px; /* general fallback */
font-size: 5vm; /* IE9 fallback */
font-size: 5vmin;
}
For browser support, you can check canIuse
I adapted a piece of CSS i wrote for a different project to solve your problem: JSFiddle DEMO
I achieved the aspect ration of 4-3 by using a .75 multiplier (i.e. the width of main is 50% and the height should be 75% of that so the padding-top is 37.5%). You can see how these are adjustable to lock in your ratio.
.main {
width: 50% !important;
height: 0;
display: block;
overflow: hidden;
line-height: 0;
position: relative;
padding: 37.5% 0 0 0;
background-color: #000;
margin: 0 auto;
}
.main-inner {
position: absolute;
display: block;
margin: auto;
top: 10px;
left: 10px;
bottom: 10px;
right: 10px;
}
.main-inner-relative {
position: relative;
padding: 10px;
}
p {
color: white;
padding: 0;
margin: 0;
}
This would require you to modify your HTML like so:
<div class="main">
<div class="main-inner">
<div class="main-inner-relative">
<p>hello</p>
</div>
</div>
</div>
reference 1 -- this reference is my original solution used to keep images locked into an aspect ratio responsively
reference 2