How to display an image progressively by chunks? - css

I'm trying to make a loading animation for a website : an image full of candles, each candle's appearing as the website loads. My mains objectives are :
Use minimal bandwidth
Maximize the picture quality
Create transition between each parts of the picture
So,
I tried to use the Photoshop legacy Export for Web features but the alpha layer (transparency) that I want to use in PNG is then tessellated, they are also subtle noise on some images.
I thought about using a video with embedded transitions but even a VP9 video is bigger than the PNG trick.
Using JPEG as well doesn't look great as I can't use transparency and the sum of all pictures are bigger than the PNG trick.
So that's why I wanted to know if someone had any idea how to do such thing ! I would be please to add more information about this if asked !
Edit
Here's the first picture of the sequence (without any edit to add transparency) :
And here's the last, there's 19 images total :

If you want to light each candle individually, I wouldn't know how to do that without using too many images, hence too much bandwidth. I can therefore not provide a solution that perfectly suits your requirements. I will, however, suggest an interim solution that might work until someone comes up with something better.
Idea:
Group the 30+ candles into three chunks, as they seem to be arranged in roughly three rows.
Although a bit tricky, the rock geometry does lend itself to be cut out accordingly as well.
Breakdown:
Create four pictures / layers:
All dark
One row lit
Two rows lit
All rows lit
Use JPG, as small as possible (maybe 1280px in width, ~70% quality)
Instead of using img, use one div with CSS background-image per layer
Place all layers on top of each other (layer 4 on top) with position: absolute
Use JavaScript to fade in another layer whenever a third of your page has loaded
The reason for using background-image is that you can easily use CSS to stretch the div containers to 100% width and height (assuming that the whole thing is supposed to be full screen) and make the images adapt to any resolution and aspect ratio easily. Upscaling a 1280px wide JPG to 1920px usually looks pretty okay, but you will have to play with image size and JPG quality to hit the sweet spot. With my suggested setup (see above), all four images should end up being about 400 to 500 KB combined.
Example:
Note that this is a rough mockup based on your first and last frame - you can surely do much better with the original material at hand.
var timer = new Array(3);
var fader = new Array(3);
var layer = new Array(3);
function fade() {
for (var i=0; i<3; ++i) {
layer[i] = document.getElementById("s" + (i+1));
layer[i].style.opacity = 0.0;
clearTimeout(timer[i]);
clearInterval(fader[i]);
start(i);
}
}
function start(i) {
timer[i] = setTimeout(function() {
fader[i] = setInterval(opacity, 20, i);
}, (2000*i));
}
function opacity(i) {
var style = window.getComputedStyle(layer[i], null);
var opacity = parseFloat(style.getPropertyValue("opacity"));
if (opacity >= 1) {
clearInterval(fader[i]);
} else {
layer[i].style.opacity = (opacity + 0.01);
}
}
.container {
position: relative;
width: 600px;
height: 417px;
}
.candles {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0.0;
background-size: 100%;
background-repeat: no-repeat;
background-position: center center;
}
#s0 {
background-image: url(http://i.imgur.com/ZQqeebI.jpg);
opacity: 1.0;
}
#s1 {
background-image: url(http://i.imgur.com/ah7UP3x.jpg);
}
#s2 {
background-image: url(http://i.imgur.com/zLgBA5x.jpg);
}
#s3 {
background-image: url(http://i.imgur.com/ar4w18n.jpg);
}
button {
width: 600px;
padding: 0.4em;
}
<div class="container">
<div id="s0" class="candles"></div>
<div id="s1" class="candles"></div>
<div id="s2" class="candles"></div>
<div id="s3" class="candles"></div>
</div>
<button onclick="fade()">Fade</button>
Here is an external link to the fiddle.

Related

iOS 13 Safari: Bug(s) in window.innerHeight

As I understand it, the window.innerHeight should return the size of the viewport without the browser chrome (address bar, navigation, tabs, etc.). But this doesn't seem to be the case in the latest version of iOS13. Instead there are two problems:
(Sometimes* too small in portrait) If you rotate from portrait mode to landscape mode with no tabs open and then back to portrait mode, the window.innerHeight value ends up being too small (by about the size of the bottom navigation bar) giving this horrible white bar at the bottom of the screen. See this discussion on macrumors for more details: https://forums.macrumors.com/threads/is-this-a-mobile-safari-bug-white-space-appears-at-bottom-after-rotating-iphone.2209551/
(Sometimes* too big in landscape) If you have a bunch of tabs open, "Show tab bar" turned on and then rotate from portrait mode into landscape mode, then window.innerHeight is too big and the bottom of the screen gets cut off.
Even after turning on every conceivable viewport tag and all permutations thereof, it doesn't seem to work. I've also looked at several "tutorials" on how to handle this problem in iOS Safari, and to date every one that I've checked is broken.
I've also tried all variations of the window.innerHeight, with more or less the same result:
The new visual viewport API returns the same results, no different than window.innerHeight. Bottom is still truncated in landscape with tab bar and portrait mode still has the white bar at the bottom.
document.documentElement.clientHeight with various permutations of CSS (using 100vh, 100%, etc.) gives the same result. Ditto for getBoundingClientRect on various divs and combinations of div elements.
window.outerHeight and screen.height give the size of the full screen without browser chrome, which is generally too big and causes an overflow.
Also tried a bunch of other random things that I've forgotten by now (should have taken notes).
You can manually fudge it on a per-device basis if you can guess the size of the top and bottom browser chrome, but this is extremely fragile. I'm looking for a solution that doesn't involve building a giant look up table of every iOS device and software configuration.
I'm trying to make a fullscreen canvas element for a web game and this issue is blocking my ability to ship. As far as I know this issue is only present in iOS13. After looking around for weeks I still haven't found a good fix.
I have had the same issue recently and I was able to solve it like this:
CSS (only relevant parts shown):
html {
height: 100%;
min-height: 100%;
max-height: 100%;
background: #99f; /* Safari for iOS and Opera for Android in fullscreen mode?!?! */
}
body {
padding: 0;
margin: 0;
color: #000;
width: 100%; /* I was desperate! This was a wild guess... And worked! */
height: 100%;
min-height: 100%;
max-height: 100%;
overflow: hidden;
background: #99f;
}
TypeScript (only relevant parts shown):
// Assume everything here is in the global scope
function detectIOSOrSafari(): boolean {
// https://stackoverflow.com/q/9038625/3569421
if ((navigator.userAgent.indexOf("Chrome") <= 0 && navigator.userAgent.indexOf("Safari") >= 0) ||
(navigator.userAgent.indexOf("Mac") >= 0 && ("ontouchend" in document)))
return true;
switch (navigator.platform) {
case "iPad Simulator":
case "iPhone Simulator":
case "iPod Simulator":
case "iPad":
case "iPhone":
case "iPod":
return true;
}
return false;
}
const isIOSOrSafari = detectIOSOrSafari();
function adjustWindowSize(): void {
let widthCss = window.innerWidth,
heightCss = window.innerHeight;
if (document.documentElement && ("clientWidth" in document.documentElement)) {
widthCss = document.documentElement.clientWidth;
heightCss = document.documentElement.clientHeight;
}
if (isIOSOrSafari) {
let bodyRect: DOMRect = null;
// Another act out of desperation...
if (document.documentElement && ("getBoundingClientRect" in document.documentElement))
bodyRect = document.documentElement.getBoundingClientRect();
else if (("getBoundingClientRect" in document.body))
bodyRect = document.body.getBoundingClientRect();
if (bodyRect) {
widthCss = bodyRect.right - bodyRect.left;
heightCss = bodyRect.bottom - bodyRect.top;
}
}
// Rest of the code, where I use widthCss and heightCss to compute my canvas' size
}
window.onresize = adjustWindowSize;
You can check out the entire source code in the project's repository: https://github.com/carlosrafaelgn/pixel

Is there a way to position page content UNDERNEATH or ON TOP OF a scrollbar?

I'm trying to emulate the CTRL+F functionality from Chrome that highlights matches on the page in the scrollbar, but for certain fields in a form. Using page offsets and percentages, I have blocks of color which correspond to the relative locations of those fields on the page.
In my prototype, the blocks of color sit to the left of the scrollbar. Ideally, they'd sit UNDERNEATH the scrollbar, and the scrollbar's track would be transparent so that it looks like they're part of the scrollbar track.
Can the default scrollbar be set to allow for overflow content to show underneath it (or allow page content to go over it)? I know this could be accomplished if I just rolled my own scroll, but I'd like to use the default ones provided by the browser if at all possible.
It's clearest if you just look at this Prototype.
CSS:
::-webkit-scrollbar {
width: 14px;
height: 18px;
background-color:transparent;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-track-piece {
background:none;
}
::-webkit-scrollbar-thumb {
height: 6px;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-border-radius: 7px;
background-color: #333
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-corner {
background-color: transparent;
}
I thought of rendering the matches on the trackbar similarly to what browsers do today before. The idea is simple by using linear-gradient background for the ::-webkit-scrollbar-track. However I did not try implementing this. Right after reading your question, I've tried it and looks like it's not such easy.
You can use the linear-gradient background OK, but if you try rendering more than 1 match (a line), it sometimes can't be rendered (especially when the window's size is changed) and the line is not rendered smoothly. Such as this seems to be OK:
//render 2 lines, one is at 50px and the other is at 100px
background: linear-gradient(transparent 50px, red 50px, red 51px, transparent 51px,
transparent 100px, red 100px, red 101px, transparent 101px);
but it's not stable, as I said when you try resizing the window, at some size, some line won't be rendered (at least I tried on Opera). When the window's height is large, the line even becomes blurred (not sharp) and thicker. I don't really understand this, because the color stops are set fixedly (by px, not by %). This issue is even worse when the number of lines is larger. You have a linear-gradient with many corresponding color stops. That seems to be a neat way to solve the problem. Just because of the undesired issue, we can't use that approach.
The new approach: So I tried using multi-backgrounds feature instead. Each background just renders 1 line, the background-size is the same for all the background is just about 2px height and the background-position should be different. Here is the equivalent code (to the above clean code) using this approach:
background: linear-gradient(red, red), linear-gradient(red, red);
background-repeat: no-repeat;
background-size: 100% 2px;
background-position: 0 50px, 0 100px;
The new approach of course requires that the browser has to support multi-backgrounds features (looks like just IE8- do not support this cool feature).
So that's almost what you need to solve this problem. Now we need to find how to apply that style using script. We can't select a pseudo-element (or something like that) via script. We can just use the window.getComputedStyle() method to get the read-only style of a pseudo-element. However we always have a way to modify the CSS directly. That's is by using pure JS with the help of document.styleSheets and cssRules. They allow us to insert/remove/modify a rule.
That looks great. But there is still another issue. When changing the style using that method, the style is not applied right (at least it happens to the ::-webkit-scrollbar-track, it may not happen to other elements). Only when you move the mouse over the scrollbar, the new style is applied. I've just found a simple way to invalidate that scrollbar by setting the overflow of document.documentElement (the html) to hidden and set it back to auto. That works almost well.
Here is the code:
var requiredTb = $(".required input");
var invalids = requiredTb;
var offsets = [];
//init offsets to highlight on the trackbar later
requiredTb.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
//find the rule styling the -webkit-scrollbar-track
//we added in the CSS stylesheet, this is done just 1 time
var sheets = document.styleSheets;
var trackRule;
for(var i = 0; i < sheets.length; i++){
var rules = sheets[i].cssRules || sheets[i].rules;
for(var j = 0; j < rules.length; j++){
var rule = rules[j];
if(rule.selectorText == "::-webkit-scrollbar-track:vertical"){
trackRule = rule; break;
}
}
}
//define an invalidate() method, we need to use this method
//to refresh the scrollbars, otherwise the newly applied style is not affected
window.invalidate = function(){
$(document.documentElement).css('overflow','hidden');
setTimeout(function(e){
$(document.documentElement).css('overflow','auto');
},1);
};
//this is the main function to set style for the scrollbar track.
function setTrackHighlights(positions, color){
positions.sort();//ensure that the input array should be ascendingly sorted.
trackRule.style.cssText = "";
var gradient = "background: ", backPos = "background-position: ";
var winHeight = $(window).height();
$.each(positions, function(i,pos){
gradient += "linear-gradient(" + color + ", " + color + "),";
backPos += "0 " + pos + "%,"
});
gradient = gradient.substr(0,gradient.length-1) + ";";
backPos = backPos.substr(0,backPos.length -1) + ";";
trackRule.style.cssText += gradient + backPos + "background-repeat:no-repeat; background-size:100% 2px";
invalidate();
}
//initially set the highlights on the trackbar
setTrackHighlights(offsets,'red');
//handle the oninput event to update the highlights accordingly
requiredTb.on('input', function(e){
var required = $(this).closest('.required');
var refreshHighlights = false;
if(this.value && !required.is('.ok')) {
required.addClass('ok');
refreshHighlights = true;
invalids = invalids.not(this);
}
if(!this.value && required.is('.ok')) {
required.removeClass('ok');
refreshHighlights = true;
invalids = invalids.add(this);
}
if(refreshHighlights){
offsets.splice(0);
invalids.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
setTrackHighlights(offsets,'red');
}
});
You have to add an empty ::-webkit-scrollbar-track:vertical rule (we need to deal only with the vertical scrollbar) in the CSS code, it should be appended at the last to override any similar rule before. We can in fact use the insertRule() method (of a CSSRuleList which can be accessed via cssRules property) to add a new rule instead of looping through the styleSheets, and through the cssRules (of each sheet) to find the empty rule ::-webkit-scrollbar-track:vertical.
The code I posted here can be improved, such as you can add another method setTrackHighlights to allow to add more lines (instead of rendering all the lines each time we need to add/remove just 1 line)...
Note that by using the term line, I mean the rendering representation of a match on the trackbar.
Demo

CSS Skew only container, not content

I'm having trouble figuring out how to make the following layout work. I'm not restricted to pure CSS - I know JS will be involved to make it cross-browser - but a CSS solution would be awesome. Here's what I am trying to achieve:
I've tried the following code, skewing the container and then skewing the image in the opposite direction, but it just gives me a square image. Chrome inspector shows me that the container is being skewed properly, but skewing the image back makes it square again. Adding an overflow:hidden to the container kind of works but the edges of the angle become jagged. Here's what I have tried:
http://codepen.io/anon/pen/ubrFz
Please help! :)
Need to tweak the positioning and the size of the container so you can crop it, and apply the backface-visibility rule:
.skew {
-webkit-backface-visibility : hidden; /* the magic ingredient */
-webkit-transform : skew(16deg, 0);
overflow : hidden;
width : 300px;
height : 260px;
position : relative;
left : 50px;
border : 1px solid #666
}
.skew img {
-webkit-transform : skew(-16deg, 0);
position : relative;
left : -40px;
}
http://codepen.io/anon/pen/HLtlG <- before (aliased)
http://codepen.io/anon/pen/wnlpt <- after (anti-aliased)
In lieu of a CSS solution, you could also achieve the effect by using a canvas and some JS; and compositing a series of cropped images onto that canvas. The benefit of the canvas method being that you'll potentially get smoother edges on the crops, and it is potentially a bit better supported.
A canvas element in HTML;
<canvas id="mycanvas"></canvas>
And JS;
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = '../my/image1.jpg';
img2.src = '../my/image2.jpg';
img3.src = '../my/image3.jpg';
var can = document.getElementById("mycanvas");
var ctx = can.getContext('2d');
var imgs = [img1, img2, img3]; //array of JS image objects that you've set up earlier
can.width = 1000;
can.height = 100;
for (var i=0; i < imgs.length; i++) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(800 - (200 * i), 0);
ctx.lineTo(900 - (200 * i), 100);
ctx.lineTo(0, 100);
ctx.closePath();
ctx.clip();
ctx.drawImage(imgs[i], 0, 0);
}
The code is just off the top of my head - I haven't tested it. But basically - lets say you have a canvas that is a maximum of 1000px wide and 100px high. What happens above is, you set up a clipping area with a diagonal line across the canvas from point (800,0) to (900,100) and then draw the image into that clipping area... Then set up a new clipping path 200 pixels shorter for each image (note the '200 * i' bit).
Obviously the math needs to be adjusted for an arbitrary number of images and so on... But the idea is there.
A bit trickier than pure CSS maybe - but as I said - possibly a bit better supported cross-browser (IE's notwithstanding...).
EDIT
Did a quick test - looks like you need to set the canvas dimensions - and also obviously wait for all images to load properly before you can composite them on the canvas.

Large background images using css

How can I load images to cover the whole background like some websites, using CSS. Not the usual background-image property but I want to load the images quickly.
Examples:
http://www.marinayachting.it/
http://alexandraowen.co.nz/
background-image is the only way to place images in CSS. If you want it to be vary large put it on the body element or a container div that fills the entire viewport.
body {
margin: 0;
padding: 0;
width: 100%;
background-image: url('my_big_image.jpg') norepeat;
}
If you use a container div you can set position:fixed; top:0; left:0 and the image will remain stationary when the page scrolls.
There's no magic to it. As far as getting it to load quickly I don't think there's much you can do if it doesn't repeat. If it does repeat then make sure your image is the size of one module. This can be as little as one pixel tall or wide depending on the content.
There is no magic to making a background image load quickly, you just:
Have a fast server.
Compress the image as much as possible.
Make your page HTML small so that the rest can start loading as soon as possible.
Don't have many other images that also has to load.
Don't have a lot of scripts and other external files that has to load.
I found this tutorial helpful. ->
http://css-tricks.com/perfect-full-page-background-image/
Bing is loading a normal background image with a fixed size. It´s not particularly fast (for me...), but perhaps it seems fast because the image is cached after the first time you load it.
You can set the style inline so that the image can start downloading without waiting for any css file to be ready.
If you set an image let's say a picture as a background you need to make it large enough to accommodate large screen sizes. You don't want the experience on your site to be, that your picture repeats multiple times on the screen. Probably at the least width should be 1260px. If background is just a simple gradient, you can cut a small part of it in photoshop and apply it on the body like this:
body {
margin:0;
padding:0;
background:#fff url(your/image/location.jpg) repeat-x scroll 0 0;
}
This method could be applied to divs too, Good luck.
In your second example site, alexandraowen.co.nz, if you took a second to look at the JS they use, you would have seen the following:
// backgrounds --------------------------------------------------------------//
var Backgrounds = {};
Backgrounds.init = function()
{
$('body').each
(
function()
{
var imgsrc = $(this).css('background-image');
if(imgsrc != 'none')
{
imgsrc = imgsrc.slice( imgsrc.indexOf('(') + 1 , -1);
$(this).css('background-image', 'none');
$(this).prepend('');
if($.browser.msie)
{
// ie 7 is the slow kid and we have to strip out quote marks ffs!
$(this).find('div.bg img').attr('src', imgsrc.split('"').join(''));
}
else
{
$(this).find('div.bg img').attr('src', imgsrc);
}
}
}
);
Backgrounds.resizeHandler();
$(window).resize(Backgrounds.resizeHandler);
$('div.bg img').load(Backgrounds.resizeHandler);
}
Backgrounds.resizeHandler = function()
{
var w = $(window).width();
var h = $(window).height();
$('div.bg img').each
(
function()
{
var wr = w / $(this).width();
var hr = h / $(this).height();
var r = Math.max(wr, hr);
var imgw = Math.round($(this).width() * r);
var imgh = Math.round($(this).height() * r);
$(this).width( imgw );
$(this).height( imgh );
var l = Math.round((w/2) - (imgw/2));
$(this).css('margin-left', l+'px');
}
);
}
As well as the HTML on the page:
<body style="background-image: none; ">
If you dig into their scripts a bit more, you can see what they did. But I guarantee you it's nothing faster than just setting the background-image property.
<img id="foo" src="bar" alt=""> with #foo { width: 100%; height: 100%; }(use position: absolute; / position: relative; & z-index for layering as desired)
Here's an old example.

scaling background and min size

Hi I have big background images 1200 by 1200, what i would like to do is expand the images when the user resizes the browser but constrain it so they never scale any smaller than the original size.
There all lots of scalable bg images out there but none I can find that do this, any help would be appreciated.
background-size: cover;
there you go
The following CSS + Javascript + JQuery combination has worked for me.
Thanks to ANeves for the above answer which was helpful.
CSS:
body
{
background: url(background.jpg) center center fixed;
background-size: 100% auto;
}
Javascript (JQuery):
jQuery (function ($)
{ // ready
$(window).resize (function (event)
{
var minwidth = 1200;
var minheight = 1024;
var bodye = $('body');
var bodywidth = bodye.width ();
if (bodywidth < minwidth)
{ // maintain minimum size
bodye
.css ('backgroundSize', minwidth + 'px' + ' ' + minheight + 'px')
;
}
else
{ // expand
bodye
.css ('backgroundSize', '100% auto')
;
}
});
});
another possibility would be to use media queries. set the image size to be say..1280x450 and then use a media query of background-size:100% auto; for all window sizes over 1280px wide.
there is some code which also handles image resizing with IE, however ive admittedly had limited success with it...
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/beach_2.png',sizingMethod='scale');
-ms-filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/beach_2.png',sizingMethod='scale')" ;
if anybody has any add ons or fixes for the above then feel free to contribute.
hope this helps someone out there...
For background images, I usually set a minimum width for the html tag which would be the original width of the image or less, this stops the image from shrinking too far.
Put image in div and give size to it.
Apply following property to img tag
background-repeat: no-repeat;
background-size: 100% 100%;
You seem to be looking for CSS3's background-size property. You can constraint the minimum size to the container, if you know that the images are 1200x1200.
I don't believe it'll work on IE though, so it might not be an option for you.
In that case, I suggest you take a peek at http://www.markschenk.com/cssexp/background/scaledbgimage.html

Resources