I'm trying to make a normal HTML5 <button> that has an animated radial timer as a background.
My use case will be a button that refreshes a view. You can click it to refresh (thereby also restarting the timer), but the view will automatically refresh once every two minutes. This timer in the background of the button will serve as an indicator of how long it has been since it last refreshed and how long it will be until it automatically refreshes again.
I only need it to work in reasonably recent versions of Chrome, Firefox, and Safari. Don't sweat IE.
I managed to do what I wanted using SVG and a <polygon> that had its points recalculated using requestAnimationFrame, but it didn't work well in Firefox and it caused my MBP's fan to kick on. I'm sure I could do this in a <canvas>, but isn't there some way to do this using only CSS?
I know that the effect may be hard to visualize with my description above, so here are some examples that are close to what I'm trying to achieve.
These two use <canvas>, but you should be able to get the idea. I'm not looking for anything that fancy, though. A solid color is fine.
This one comes very close, but it uses SVG. Even though the animation is achieved with a CSS transition, the SVG is still taxing my CPU.
One final note is that I'm trying to make a background, not an overlay. Text (or in my case a fontawesome glyph) will sit on top of the background.
See if you can do anything with these classes and script.
var myCounter = new Countdown({
seconds: 120, // number of seconds to count down
onUpdateStatus: function (sec) {
}, // callback for each second
onCounterEnd: function () {
} // final action
});
function Countdown(options) {
var timer,
instance = this,
seconds = options.seconds,
updateStatus = options.onUpdateStatus,
counterEnd = options.onCounterEnd;
function decrementCounter() {
updateStatus(seconds);
if (seconds === 0) {
counterEnd();
instance.stop();
}
seconds--;
}
this.start = function () {
clearInterval(timer);
timer = 0;
seconds = options.seconds;
timer = setInterval(decrementCounter, 1000);
};
this.stop = function () {
clearInterval(timer);
};
}
myCounter.start();
.circle {
position: relative;
margin: 7em auto;
width: 16em; height: 16em;
border-radius: 50%;
background: black;
}
.arc {
overflow: hidden;
position: absolute;
top: 0em; right:50%; bottom: 50%; left: 0em;
transform-origin: 100% 100%;
transform: rotate(90deg) skewX(30deg);
}
.arc:before {
box-sizing: border-box;
display: block;
border: solid 8em grey;
width: 200%; height: 200%;
border-radius: 50%;
transform: skewX(-30deg);
content: '';
}
<div class="circle">
<div class="arc"></div>
</div>
Related
Currently I have two checkboxes that control the speed of an animated fan giving me four speeds (no checks = speed 0; each checkbox checked individually = speed 1 and 2; and both checked together = speed 3). Now I need it to be one button that cycles through each speed. I also need a second button that reverses the fan, but I can wory about that later.
It sounds like the solution you desire requires javascript, and so I would refer you straight away to the second example in the code snippet I provided. There is also the first code snippet to see what this would look like using CSS, although CSS by itself won't be able to keep track of how many times you have clicked, so the second you will not be able to get the animation to speed up/slow down again and again.
const myDiv = document.querySelector("#javascript");
myDiv.addEventListener("click", function() {
// Gets the current CSS style
const currentDuration = parseFloat(myDiv.style.animationDuration[0]);
// Makes it faster by cutting the time to loop in half
const newDuration = currentDuration/2;
// Uses a string literal to change the CSS
myDiv.style.animationDuration = `${newDuration}s`
})
#keyframes myanimation {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: red;
}
}
p {
background-color: red;
width: 100px;
height: 100px;
animation: myanimation 4s infinite;
}
p:active {
border: 10px solid black;
animation-duration: 8s;
}
#javascript {
width: 100px;
height: 100px;
background: red;
animation: myanimation infinite;
}
CSS Solution (No memory)
<p>
</p>
JS Solution (With memory)
<!-- The styling you want to change must be in-line CSS for the Javascript to read it (To the best of my knowledge)-->
<div style="animation-duration: 4s" id="javascript">
</div>
I want to do an animation assembled from multiple gifs loaded in a web page.
So, I will put a gif on top of another to make that.
In order for that to work the gifs will need to load at the exact same time.
How do I do this in web programming?
My fiddle https://jsfiddle.net/baz3ynt1/9/ is an answer using javascript to do the image loading asynchronously, but then adding them to the DOM at the same time in order to start their animation synchronously. I don't think you can force the browser to finish loading an image at a certain time, as the browser can't know how long it will take to load a resource.
Each Gif gets loaded using
gif = new Image();
gif.src = 'image url';
gif.onload = handleLoading();
and the handleLoading() function triggers a startAnimation() function as soon as all Gifs triggered their onload event:
function handleLoading()
{
// numLoadedGifs is a counter previously initialized as zero
// gifUrls is an array of the urls to load
numLoadedGifs++;
if (numLoadedGifs === gifUrls.length)
{
// now all images are completely loaded
startAnimation();
}
};
Then the startAnimation() function appends the previously created img elements (stored in an array) as children onto a <div id="animation">, but in order to make them run at the same time their src attribute gets reset and set again:
function startAnimation()
{
var animationDiv = document.getElementById('animation');
for (var index in gifList)
{
var img = animationDiv.appendChild(gifList[index]);
img.classList.add('cloth');
img.src = '';
img.src = gifUrls[index];
}
};
I tested it in Firefox and IE 11 (resetting the src is what makes it work in IE 11).
Edit: Apparently IE isn't always fast enough to append the images and then reset their src's in one step, so https://jsfiddle.net/baz3ynt1/10/ splits the two tasks:
function startAnimation()
{
var animationDiv = document.getElementById('animation');
for (var index in gifList)
{
var img = animationDiv.appendChild(gifList[index]);
img.classList.add('cloth');
}
for (var index in gifUrls)
{
gifList[index].src = '';
gifList[index].src = gifUrls[index];
}
};
the gifs will need to load at the exact same time
There is a technique called CSS Spriting.
Instead of loading 4 100x100 pixel GIFs (or PNGs), you load a single 200x200 pixel GIF and then in a series of 100x100 pixel divs, you reposition the background-image, so that it shows only the part of the 200x200 pixel image that you want to display:
.box {
float: left;
width: 100px;
height: 100px;
margin: 6px 12px 6px 0;
background-image: url(http://placehold.it/200x200);
}
.top-left {
background-position: 0 0;
}
.top-right {
background-position: -100px 0;
}
.bottom-left {
background-position: 0 -100px;
}
.bottom-right {
background-position: -100px -100px;
}
.original {
clear: left;
width: 200px;
height: 200px;
}
<div class="box top-left"></div>
<div class="box top-right"></div>
<div class="box bottom-left"></div>
<div class="box bottom-right"></div>
<div class="box original"></div>
If I create an isometric grid of tiles using only HTML (each grid item being a diamond-shaped image), the tiles overlap on the corners. So, clicking on one will likely click the image that is overlapping it.
I can use JavaScript to get the X/Y of the mouse click event and determine which image was clicked. I can use HTML5 and, similarly, translate the X/Y of the click into an image.
I'm looking into using SVG to rotate images 45 degrees. Then, they don't overlap. I can use an on-click on the SVG objects. So far, this appears to be the simplest method of handling click events in isometric view in HTML.
Is there a method of displaying non-square objects in HTML that I've overlooked?
Long time since this question was asked, so most probably you already found the answer that you were searching for, but I would like to clarify it for anyone reaching here with the same doubt.
If you apply CSS transformations to an HTML element, you don‘t need to make any JavaScript calculation to know if it was clicked. Looking at your comment it seems that you think that the mouse events work in the element boundary box instead of the element itself, but it doesn‘t work in that way. The mouse events are triggered when the area respective to the element is clicked, respecting its transformations.
Take a look at this small snippet. The tiles have been transformed with CSS transformations, click on the tiles so you can check that the events are triggered taking into account the diamond shape of each one.
document.querySelectorAll('.isometric').forEach(tile => {
tile.addEventListener('click', function () {
this.dataset.active = 1 - (this.dataset.active || 0);
});
});
body, html {
height: 100%;
margin: 0;
padding: 0;
}
.container {
left: 50%;
position: relative;
top: 50%;
transform: translateY(-100px);
}
.isometric {
background-color: #CCC;
height: 100px;
outline: 2px solid #FFF;
position: absolute;
width: 100px;
}
.isometric[data-active="1"] {
background-color: #F00;
}
<script src="https://unpkg.com/isometric-css#2.2.3/index.js"></script>
<div class="container">
<div class="isometric" data-view="top" ></div>
<div class="isometric" data-view="top" data-right="100"></div>
<div class="isometric" data-view="top" data-left="100" data-left="100"></div>
<div class="isometric" data-view="top" data-right="100" data-left="100"></div>
</div>
I've searched around quite a bit and I'm fairly certain this doesn't exist, I'm mainly looking to confirm that. What I'd like to do is have a div that makes everything behind it transparent -- similar to what canvas' destination-out compositing option does.
For a little more context, here's the situation. I have an OpenGL window drawing behind a QtWebKit overlay. The OpenGL window has multiple "subwindows" that can be overlapping, which are decorated using the WebKit overlay. When they overlap though, because of this two layer system, the decorations for the overlapped windows do not get occluded.
The backup option is just to use a full-window canvas for this (the window trimmings are fairly simple), but it would be nicer not to. Note that because this is an embedded WebKit instance, it doesn't need to be cross-browser, and something WebKit (or QtWebKit) specific is fine.
EDIT
I can't answer my own question within 24 hours, so here's my solution, with thanks to #Kevin Peno
The following is a simplified version of what I was looking for. It creates two divs "visible" and "invisible". "invisible" masks off "visible" so that it displays the background image behind it instead of the "visible" div.
The real keys are -webkit-mask-image (http://www.webkit.org/blog/181/css-masks/) and -webkit-canvas (http://www.webkit.org/blog/176/css-canvas-drawing/), so this will only work with webkit-based browsers.
HTML:
<html>
<body>
<div id="visible"/>
<div id="invisible"/>
</body>
</html>
JavaScript:
function updateMask()
{
var w = $("#visible").width();
var h = $("#visible").height();
var context = document.getCSSCanvasContext("2d", "mask", w, h);
context.fillStyle = "rgba(255, 255, 255, 1.0)";
context.fillRect(0, 0, w, h);
var my_off = $("#visible").offset();
var inv_off = $("#invisible").offset();
var rel_left = inv_off.left - my_off.left;
var rel_top = inv_off.top - my_off.top;
context.clearRect(rel_left, rel_top, $("#invisible").width(), $("#invisible").height());
}
$(window).ready(function()
{
updateMask();
$("#invisible").draggable();
$("#invisible").bind("drag", function(e, ui)
{
console.log("drag");
updateMask();
e.preventDefault();
});
});
CSS:
body
{
background-image: url(http://www.google.com/images/logos/ps_logo2.png);
}
#visible
{
position: absolute;
background-color: red;
z-index: 0;
width: 1000px;
height: 1000px;
top: 0;
left: 0;
-webkit-mask-image: -webkit-canvas(mask);
}
#invisible
{
position: absolute;
z-index: 1;
width: 100px;
height: 100px;
top: 50px;
left: 50px;
cursor: move;
background-color: rgba(0, 255, 0, 0.5);
}
Here's a blog post about using css to apply an image mask to an element. It sounds pretty close to what you are looking for or will at least be good for some ideas. Let me know how it works out.
CSS Masks
I was wondering, what is the best way (using html, css, and graphics) to create a web page whose top header section appears to be beveled, as opposed to straight across? Please see the below image as an example:
I'm not sure how to use images in a way such that they would expand/contract in accordance with different browser sizes/resolutions...
Can anyone offer me some help? Or perhaps point me to a resource?
Thanks!
You could use border-radius.
See my example on jsFiddle.
Mine is a cleaner version of #Alex's:
Live Demo
.head {
-webkit-border-top-left-radius: 40% 80px;
-webkit-border-top-right-radius: 40% 80px;
-moz-border-radius-topleft: 40% 80px;
-moz-border-radius-topright: 40% 80px;
border-top-left-radius: 40% 80px;
border-top-right-radius: 40% 80px;
background: blue;
height: 280px
}
<div class="head"></div>
It obviously won't work in IE.
You could use CSS3 or webkit-specific properties, but this is not well supported as far as cross-browser compatibility is concerned. If you want to support as many browsers as possible, your best bet would be to use a background image to achieve this effect.
Here's a cross-browser version, which i made with help of jquery. Basically, the script creates many spans, with white background and decreasing width.
You can play around with STEPS and FACTOR variables, which will change the result. The step function sets the easing of the curve. You may replace it later with better functions than mine, it's just an example.
var STEPS = 53;
var FACTOR = 5;
var $el = $('div.header');
var width = $el.outerWidth();
var $span = $('<span></span>');
for(i=0;i<STEPS;i++){
tmpWidth = stepWidth(i, width);
$span.clone().css({
'bottom': i + 'px',
'width': tmpWidth,
'left': (width - tmpWidth)/2
}).appendTo($el);
}
function stepWidth(i, width){
return -(1 / FACTOR * Math.pow(i, 2)) + width;
}
You can find the entire code (html + css on the Fiddle)
Here is another way of achieving this.
Draw an overlay with pseudo element with width and height larger than element itself.
Apply border-radius to create round effect and background-color.
Add overflow: hidden on parent to hide excess part.
Output Image:
body {
background: linear-gradient(lightblue, blue);
min-height: 100vh;
margin: 0;
}
.box {
position: relative;
margin: 5vh auto;
overflow: hidden;
height: 90vh;
width: 500px;
}
.box:before {
border-radius: 100% 100% 0 0;
position: absolute;
background: white;
bottom: -200px;
right: -200px;
left: -200px;
content: '';
top: 0;
}
<div class="box">
</div>