I want to create a media control bar for a video player with the controls blurring the background. Currently I can get the glassy look but I can't get blurring to work.
I tried following this guide: https://medium.com/#AmJustSam/how-to-do-css-only-frosted-glass-effect-e2666bafab91 but it seems like it only works on static images.
Here's an example of what I want:
I also saw the webkit backdrop-filter which looks perfect but it's only stable on safari so I can't use that. Any advice for frosted glass on dynamic videos?
I was able to accomplish this by copying the bottom part of the video to a canvas every frame and blurring the canvas via CSS. It seems to work well in Chrome, but flashes sometimes in Firefox. Doing the blur programmatically in the canvas with something like Superfast Blur might be more performant, but that's an experiment for another day. Tomorrow, probably.
function initControls (player, blurRadius, controlHeight, videoWidth, videoHeight) {
// crop player to video size
let video = player.querySelector('video');
videoWidth = videoWidth || video.clientWidth;
videoHeight = videoHeight || video.clientHeight;
player.style.width = videoWidth + 'px';
player.style.height = videoHeight + 'px';
// crop control bar to video size
let controlBar = player.querySelector('.control-bar');
controlBar.style.width = videoWidth + 'px';
controlBar.style.height = controlHeight + 'px';
// canvas needs to be slightly taller than what gets displayed
// to blur cleanly
let canvas = player.querySelector('canvas');
canvas.width = videoWidth;
canvas.height = 2 * blurRadius + controlHeight;
canvas.style.filter = `blur(${blurRadius}px)`;
canvas.style.top = -2 * blurRadius + 'px';
// copy video to canvas
let ctx = canvas.getContext('2d');
let videoCropY = videoHeight - canvas.height;
function updateCanvas () {
ctx.drawImage(
video,
0, videoCropY, canvas.width, canvas.height,
0, 0, canvas.width, canvas.height
);
}
// update the canvas only when necessary
let hovering = false;
function renderLoop () {
updateCanvas();
if (hovering && !video.paused) {
window.requestAnimationFrame(renderLoop);
}
}
// no point in rendering to a canvas you can't see
player.addEventListener('mouseenter', () => {
hovering = true;
renderLoop();
});
player.addEventListener('mouseleave', () => { hovering = false; });
video.addEventListener('play', renderLoop);
}
document.addEventListener('DOMContentLoaded', event => {
// do the magic
initControls(document.querySelector('.player'), 4, 50, 320, 240);
// basic play button functionality
document.querySelector('.play-button').addEventListener('click', event => {
let v = event.target.closest('.player').querySelector('video');
if (v.ended) v.currentTime = 0;
if (v.paused) {
v.play();
} else {
v.pause();
}
});
});
/* styling required for blurred control background */
.player {
position: relative;
}
.player > video {
margin: 0;
display: block;
}
.control-bar > canvas {
margin: 0;
display: block;
left: 0;
position: absolute;
}
.control-bar {
margin: 0;
overflow: hidden;
position: absolute;
bottom: 0;
/* height of control bar is specified in javascript, sorry about that */
}
/* simple control-hiding mechanism; other methods also work */
/* javascript relies on mouseover and mouseout to decide whether to update canvas */
.player > .control-bar {
display: none;
}
.player:hover > .control-bar {
display: block;
}
/* styling actual controls; adjust to taste */
.play-button {
height: 30px;
line-height: 30px;
width: 100px;
color: white;
border: 1px solid white;
position: absolute;
top: 10px;
left: 50%;
margin-left: -50px;
text-align: center;
}
<div class="player">
<video src="https://archive.org/download/MedicalA1950/MedicalA1950_512kb.mp4"></video>
<div class="control-bar">
<canvas></canvas>
<div class="play-button">PLAY/PAUSE</div>
</div>
</div>
Related
I couldn't find an example where this image sequence animation is triggered at any other part except on the top of the website.
The animation is working fine as it is, the problem is: if i put any content above the canvas, the image sequence will still be linked to the top of the page. So when the user gets to the animation, they will see it already half way through.
I'd like to ask for your help to change the starting point of this animation to only when it enters the viewport, not necessarily on the top of the page.
I'll also leave a link to the same exact snippet running on codepen, just in case: https://codepen.io/querubim_reginaldo/pen/OJzoOXK
Thanks in advance!
const html = document.documentElement;
const canvas = document.getElementById("pro_display_animation");
const context = canvas.getContext("2d");
const currentFrame = index => (
`https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${(index + 1).toString().padStart(4, '0')}.jpg`
);
// total of image files
const frameCount = 160;
// sets the canvas size to full screen
canvas.width = document.documentElement.clientWidth * 1;
canvas.height = document.documentElement.clientHeight * 1;
// draws the first image on screen
const img = new Image();
img.src = currentFrame(1);
img.onload = function() {
context.drawImage(img, 0, 0);
};
// updates the image in sequence
const updateImage = index => {
img.src = currentFrame(index);
context.drawImage(img, 0, 0);
}
// links the scroll position with the correct image
window.addEventListener('scroll', () => {
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop / maxScrollTop;
const frameIndex = Math.min(
frameCount - 1,
Math.ceil(scrollFraction * frameCount)
);
requestAnimationFrame(() => updateImage(frameIndex + 1))
});
body {
margin: 0;
padding: 0;
}
#section_canvas_animation {
display: flex;
justify-content: center;
align-items: flex-start;
height: 1200vh;
width: 100vw;
background: #efefef;
}
canvas {
position: sticky;
top: 0;
max-width: 100vw;
max-height: 100vh;
}
.website_content {
height: 400vh;
background: darkcyan;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 100px;
}
<body>
<div id="section_canvas_animation">
<canvas id="pro_display_animation"></canvas>
</div>
<div class="website_content">THIS IS SOME WEBSITE CONTENT</div>
</body>
`
Check my demo here: https://www.sfertons.dev/lab/apple-scroll-sequence-animation
The trick is to take the "animation sequence" offsetTop into account and use its scrollHeight to calculate the maxScrollTop:
const scrollTop = document.documentElement.scrollTop - heroSequence.offsetTop
const maxScrollTop = heroSequence.scrollHeight - window.innerHeight
The project is also on my Github: https://github.com/Spharian/apple-sequence-animation
I would like to clip an animated canvas as background to a <h1 />. The requirements are:
Use an actual <h1 /> tag instead of rendered heading in canvas.
Use of images to be rendered with ctx.drawImage().
h1 {
color: transparant;
background-image: <some-canvas>;
background-clip: text;
}
There are several approaches I've tried so far with varying success:
Creating a regular canvas and setting it as background of the <h1 />-tag using -webkit-canvas and -moz-element. This approach ticked all of my requirements but unfortunatly -webkit-canvas was deprecated along with document.getCSSCanvasContext("2d") in Chromium. Safari is the only working browser.
Using the CSS Paint API (Houdini). Using a requestAnimationFrame() to update a css var I can keep ticking the animation and do the animation I would like to implement. However, in Chromium, passing in images has to be done using a workaround (instead of creating a property of type image, images have to be passed in using background-image, making me unable to use the background-image to tell CSS Paint to use my worklet as background. The spec is not entirely implemented.
Creating an inline svg as background-image: url("data:image/svg+xml;utf8,<svg><foreignObject><canvas id='..'/></foreignObject></svg>"); and trying to update that canvas using requestAnimationFrame. Does not work at all.
Are there any other methods I could try?
If it doesn't have to be a background-image, you could put the canvas element inside the h1 element, match the canvas width and height to that of the h1 element, give it a lower z-index than the text in the h1 element so that it appears to be behind the text, acting like a background.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
/*
Set the dimensions of the canvas to
match the parent h1 element
----------------------------------------------------------*/
setCanvasSize();
// (and for demonstrative purposes, scale on window resize)
window.addEventListener('resize', setCanvasSize, false);
function setCanvasSize () {
var _w = canvas.parentNode.clientWidth;
var _h = canvas.parentNode.clientHeight;
canvas.width = _w;
canvas.height = _h;
canvas.style.width = "'" + _w + "px'";
canvas.style.height = "'" + _h + "px'";
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------
All this code below is just a ball animation borrowed from MDN
to illustrate what I'm suggesting.
Source: MDN
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Advanced_animations
*/
var raf;
var running = false;
var ball = {
x: 100,
y: 100,
vx: 5,
vy: 1,
radius: 50,
color: 'blue',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function clear() {
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(0,0,canvas.width,canvas.height);
}
function draw() {
clear();
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
ball.vx = -ball.vx;
}
raf = window.requestAnimationFrame(draw);
}
if (!running) {
raf = window.requestAnimationFrame(draw);
running = true;
}
ball.draw();
/*--------------------------------------------------------*/
h1 {
/* Important for positioning the span element */
position: relative;
/* Cosmetic/demonstrative */
border: 2px solid red;
height: 100%;
text-align: center;
color: red;
font-size: 32px;
font-family: Roboto,sans-serif;
margin: 0 auto;
}
h1 span {
/* Vertically and horizontally center the text */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
/*
Text will appear above canvas as long as its
z-index > canvas' z-index
*/
z-index: 2;
}
h1 canvas {
/* this will make the canvas appear behind the text */
position: relative;
z-index: 1;
}
<h1>
<span>Lorem Ipsum etc.</span>
<canvas id="canvas"></canvas>
</h1>
Just a thought anyway, maybe you can try it out and expand on the idea to suit your needs. I'm not 100% sure what you're working on so apologies if it's not helpful.
We need to implement a screen saver which should have 3 images.
The bottom image should be fixed.
Middle image will be moving.
Top image will be moving at relatively higher speed.
We created 3 divs to handle the images as below.
<!DOCTYPE html>
<html>
<head>
<title>Moving Screen Saver</title>
<style>
html, body {
position: relative;
height: 100%;
width: 100%;
}
#bgimg {
background-image: url("background/moon_bg.png");
position: absolute;
background-repeat: no-repeat;
background-size: cover;
width: 100%;
height: 100%;
}
.animator {
width: 100vw;
height: 100vh;
}
#poster {
background-image: url("posters/gladiator.png");
position: absolute;
background-position:right 20px bottom 20px;
background-repeat: no-repeat;
width: 100%;
height: 100%;
background-color: transparent;
}
</style>
</head>
<body>
<div id="bgimg"></div>
<div class="animator"></div>
<div id="poster"></div>
<script>
var poster = document.getElementById('poster');
var animate;
function moveRight()
{
poster.style.left = poster.style.left || 0;
poster.style.left = parseInt(poster.style.left) - 10 + 'px';
requestAnimationFrame(moveRight);
}
moveRight();
function rollAnimate(element, url) {
if(!element instanceof HTMLElement)
return;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
var x = 0;
var pattern;
var framesPerSec = 10;
img.onload = startDrawing;
img.onerror = console.error;
img.src = url;
canvas.style.cssText = 'position:absolute;'
function startDrawing() {
element.insertBefore(canvas, element.firstChild);
pattern = ctx.createPattern(img, 'repeat');
resize();
anim();
window.addEventListener('resize', resize, {passive: true});
}
function anim() {
x = (x - 1) % img.width;
ctx.setTransform(1,0,0,1,x,(canvas.height) - (img.height));
ctx.fill();
setTimeout(function() {
requestAnimationFrame(anim);
}, 1000 / framesPerSec);
}
function resize(evt) {
canvas.width = element.offsetWidth;
canvas.height = element.offsetHeight;
ctx.fillStyle = pattern;
ctx.rect(0, canvas.height - img.height, canvas.width, img.height);
ctx.globalCompositeOperation = 'copy';
}
}
rollAnimate(document.querySelector('.animator'), 'buildings/mod_cropped.png');
</script>
</body>
</html>
lower image - bgimg
middle image - animator
top image - poster
We are able to see lower image which is fixed and middle image which is moving. But poster image is not visible. We are thinking that this is because when we are trying to paint middle image it is replacing top image. Can any one please help me to fix this issue.
Below is jsfiddle page
https://jsfiddle.net/n76epste/10/
So when I look, you are actually getting all 3 images. Your issue is the poster moves off screen super fast and never returns.
bgimg is static and covered by animator (you can see this by deleting animator)
animator is animated and repeating and cover bgimage
poster is given an ever updating style="left: -***px" where * is an increasing number. (You can see this for a split second when loading the page if you hide bgimg and animator with css)
This is because of Z-index. I changed z-index of poster dev tp the highest to fix the problem.
I'm trying to create a one-pager at the moment with a canvas where you can click to add/place images at random. I've gotten most of what I want to work, but being extremely new to jquery and canvas, I still can't figure out how to set a max-size for my images. As far as I've understood, it can be quite hard to make images on a canvas work perfectly when you haven't set the canvas size manually in css, but I have a resizing canvas, so I'm not really sure how I get around that.
I want the images that I place to have a max-height/max-width of 80% of the viewport. I tried setting a max size in css, but it didn't do anything.
Here's a jsfiddle
$(document).ready(function () {
var images = [];
$("#siteload").fadeIn(1500);
$('.put').each(function() {
images.push($(this).attr('src'));
});
(function() {
var
htmlCanvas = document.getElementById('c'),
context = htmlCanvas.getContext('2d');
initialize();
img = new Image();
count = 0;
htmlCanvas.onclick= function(evt) {
img.src = images[count];
var x = evt.offsetX - img.width/2,
y = evt.offsetY - img.height/2;
context.drawImage(img, x, y);
count++;
if (count == images.length) {
count = 0;
}
}
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function resizeCanvas() {
htmlCanvas.width = window.innerWidth;
htmlCanvas.height = window.innerHeight;
}
})();
});
$(".hvr_cnt").mouseenter(function () {
title = $("<div class='hvr_ttl'>" + $(this).children()[0].title + "</div>");
$(this).children()[0].title = "";
$(this).append(title);
});
$(document).mousemove(function(e){
$('.hvr_ttl').css({
top: e.pageY - $(".hvr_ttl").height()/2,
left: e.pageX - $(".hvr_ttl").width()/2
});
});
$(".hvr_cnt").mouseleave(function (e) {
$(this).children()[0].title = $($(this).children()[1]).html();
$(this).children()[1].remove();
});
body {
margin: 0;
padding: 0;
background: rgb(240, 240, 240);
width: 100%;
height: 100%;
border: 0;
color: rgb(40, 40, 40);
overflow: hidden;
display: block;
}
.slides {
display: none
}
#c {
display: block;
position: absolute;
left: 0px;
top: 0px;
}
.hvr_ttl {
display: block;
position: absolute;
pointer-events: none;
cursor: none;
}
.hvr_cnt {
cursor: none;
padding: none;
margin: none;
}
<body ontouchstart="">
<div class="hvr_cnt">
<canvas id="c" title="click"></canvas>
</div>
<ul class="slides">
<li><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/85/Smiley.svg/2000px-Smiley.svg.png" class="put" /></li>
<li><img src="https://emojipedia-us.s3.amazonaws.com/thumbs/160/google/56/thumbs-up-sign_1f44d.png" class="put" /></li>
</ul>
</body>
Any help would be appreciated :)
Try this :
htmlCanvas.onclick= function(evt) {
img.src = images[count];
img.width = 80/100* htmlCanvas.width;
img.height = 80/100* htmlCanvas.height;
var x = evt.offsetX - img.width/2,
y = evt.offsetY - img.height/2;
// context.drawImage(img, x, y);
context.drawImage(img, x, y, img.width, img.height);
count++;
if (count == images.length) {
count = 0;
}
}
context.drawImage(img, 0, 0,img.width,img.height,0,0,htmlCanvas.width,htmlCanvas.height);
you also can use background-size:cover;
The max-height/width doesn't work because everything within the canvas is independent from your css.
If you want to set the image to 80% use htmlCanvas.width and htmlCanvas.height to calculate the new size and use context.drawImage(img, x, y, w, h) to set the image size when drawing.
Maybe this post could be helpful.
I have some script on JS, it change width and height of canvas element:
function RefreshSizes (canvas) {
var temp_width = 320;
var temp_height = 240;
document.getElementById(canvas).setAttribute('width', temp_width);
document.getElementById(canvas).setAttribute('height', temp_height);
}
this functions calling right after canvas initializing.
It works fine on Chrome.
But in FireFox 49 I see that:
What could it be?
UPD#1 Tested code of BukkitmanPlays MCPE
UPD#2
Full CSS for canvas:
element {
width: 320px;
height: 240px;
}
.canvas {
border: 3px solid #E0E0E0;
z-index: 0;
position: relative;
}
html {
font: 10px/10px arial;
}
some code on one browser isn't the same on another browser, so in this case, what I would do:
function RefreshSizes (canv) {
var temp_width = 320;
var temp_height = 240;
var canvas = canv;
canvas.width = temp_width;
canvas.height = temp_height;
}
I'm sure this will work