Css jittery text translate - javascript

[SOLUTION]
Solution is to use the will-change CSS property taht forces GPU rendering:
will-change: transform;
[ORIGINAL QUESTION]
I've been digging a lot internet and found no solution to a problem that seems rather simple which is, translating a holder containing text, and having this text moving smoothly.
Here is an example of the problem, you can see the text is following its holder step by step, not smoothly:
I also made a small codepen to see the effect live :
https://codepen.io/Durss/pen/ExgBzVJ?editors=1111
var angle = 0;
var radius = 100;
function renderFrame() {
requestAnimationFrame(renderFrame);
var cx = document.documentElement.clientWidth / 2;
var cy = document.documentElement.clientHeight / 2;
var div = document.getElementById("text");
var px = cx + Math.cos(angle) * radius;
var py = cy + Math.sin(angle) * radius;
angle += .001;
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
}
renderFrame();
body {
background-color:black;
width:100%;
height:100%;
}
#text {
position:absolute;
left:0;
top:0;
border: 2px solid white;
background-color: #2f9da7;
padding: 10px;
border-radius:20px;
color: white;
font-weight: bold;
}
<div id="text">blah blah</div>
Basically, the problem is that the holder is moving at a subpixel level but the text position seems rounded no matter what i try.
I used translate3d() so it uses GPU rendering which fixes the holder's displacement but not its text content.
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
I've seen here and there the following CSS "solutions" that didn't work for me:
text-rendering: geometricPrecision;
-webkit-font-smoothing: antialiased;
transform-style: preserve-3d;
backface-visibility: hidden;
-webkit-font-smoothing:none;
I've had this problem many time in the past but always gave up fixing it,i think it's time to seek for help as a last bullet !
Thanks for reading me!

will-change: contents;
resolved my own jittery text issues when doing transform rotations. This tells the browser that the contents of an element are expected to change therefore should not be cached.
This must be applied immediately before the transform.

CSS transitions help on making sub-pixels animations smoother.
transition: all 0.001s linear;
It works fine on chrome, but seems a bit less effective on firefox.
This article may help :
Firefox CSS Animation Smoothing (sub-pixel smoothing)

As a workaround you can Math.round the px values. This would prevent the text from jittering on its own (makes the whole containing div jittery).
var angle = 0;
var radius = 100;
function renderFrame() {
requestAnimationFrame(renderFrame);
var cx = document.documentElement.clientWidth / 2;
var cy = document.documentElement.clientHeight / 2;
var div = document.getElementById("text");
var px = Math.round(cx + Math.cos(angle) * radius);
var py = Math.round(cy + Math.sin(angle) * radius);
angle += .001;
div.style.transform = "translate3d("+px+"px, "+py+"px, 0)";
}
renderFrame();
body {
background-color:black;
width:100%;
height:100%;
}
#text {
position:absolute;
left:0;
top:0;
border: 2px solid white;
background-color: #2f9da7;
padding: 10px;
border-radius:20px;
color: white;
font-weight: bold;
}
<div id="text">blah blah</div>

Related

Div following cursor paints pixels on screen in Safari (bug?)

I've used the following code to make a div that follows the cursor with easing applied to the movement:
CSS
#cursor {
position: absolute;
width: auto;
height: auto;
white-space: nowrap;
pointer-events: none;
transition: opacity 200ms;
cursor:none!important;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index:99999999;
color:white;
backface-visibility: hidden;
}
JavaScript/jQuery:
$(document).on("mouseover mousemove", function (event) {
mouseX = event.pageX;
mouseY = event.pageY - $(window).scrollTop();
cursorWidth = $cursor.width();
cursorHeight = $cursor.height();
});
requestAnimationFrame(performAnimation);
var performAnimation = function() {
var distX = mouseX - cursorX;
var distY = mouseY - cursorY;
cursorX = cursorX + (distX * speed);
cursorY = cursorY + (distY * speed);
$cursor.css({
left: cursorX + "px",
top: cursorY + "px"
});
request = requestAnimationFrame(performAnimation)
}
It works as intended in Chrome, but on Safari it leaves traces of the text in the div on top of the screen, i. e. the elements underneath the cursor z-index wise. Here's a video showing the behaviour.
Is this a known bug with Safari and can it be avoided somehow? I'm not sure if this has to do with the requestAnimationFrame() function or if it's something else. I tried applying backface-visiblity:none; to the #cursor itself through CSS, as this sometimes solves these type of issues, but in this case it's not doing anything.
The problem had to do with the #cursor being a child of a container that was fixed on top of the whole page. Removing this container and placing #cursor without a container on it's own fixed the issue.
For some reason I thought it was practical to have the cursor wrapped in this container so that I could add classes through JS to the container instead of the cursor itself to manipulate it's style. But I ended up adding the styling classes to a <span> inside #cursor instead.

How to align the popup div direction in css?

I am working on a website that while mouse over a DVD shows the details like you see in picture 1, however, it doesn't work on those DVD placed to the right of the screen as you can see in picture 2, the content got chopped.
How to let it automatically choose which direction to display the content? Like if this DVD is close to the right screen, show content to the left?
Many thanks!
.imgbox .imgbox_content{
display: none;
}
.imgbox:hover .cover{
display: none;
}
.imgbox:hover .imgbox_content{
display: block;
z-index:2;
width:600px;
border-radius: 1%;
border:1px solid gray;
-webkit-transition: 1s;
position:absolute;
background: black;
color:white;
}
Here's a quick solution using JS. Not seeing any JS/HTML though, so you'll need to adapt it to whatever your code looks like:
var imgboxes = document.querySelectorAll('.imgbox');
imgboxes.forEach(function (imgbox) {
var rect = imgbox.getBoundingClientRect(),
screen_width = document.body.clientWidth,
popup_width = <<POPUP_CONTENT_WIDTH>>;
if (rect.right + popup_width > screen_width) {
imgbox.classList.add('to_left');
}
});
and then change your CSS to something like this for the popup:
.imgbox:hover .imgbox_content{
display: block;
z-index:2;
width:600px;
border-radius: 1%;
border:1px solid gray;
-webkit-transition: 1s;
position:absolute;
background: black;
color:white;
}
imgbox:hover .imgbox_content.to_left {
/* this assumes you've got a position:relative item wrapping the imgbox and that .imgbox_content is a child */
right: 0;
}
In order to get even more complete, you can handle screen resizes too:
window.addEventListener('resize', calculate_pos_imgboxes)
var calculate_pos_imgboxes = function() {
var imgboxes = document.querySelectorAll('.imgbox');
imgboxes.forEach(function (imgbox) {
var rect = imgbox.getBoundingClientRect(),
screen_width = document.body.clientWidth,
popup_width = <<POPUP_CONTENT_WIDTH>>;
if (rect.right + popup_width > screen_width) {
imgbox.classList.add('to_left');
} else {
imgbox.classList.remove('to_left');
}
});
}
calculate_pos_imgboxes();
The easiest way to do this is to just force your popups to appear at the center of the screen. You can do this using position:absolute. However, if you want the popups bounded to the location of the item - then you have to do some calculations.
It would look like this.
Get width of your popup.
Get distance from left edge of target to right edge of screen.
Compare 1 and 2
If dist < width, then offset to the left. Otherwise, offset to the right.
The code for this would look something like (using jQuery).
var padding = 20;
var elem = $(yourpopup);
var popupwidth = elem.width();
var position = p.position();
var dist = position.left - popupwidth;
// if the popup is wider than distance to edge plus padding, then offset margin using negative value.
if (dist < popupwidth){
elem.css('margin-left', "'-" + popupwidth + padding + "px'" );
}

Two arrows rotating inside a circle

So, the basic idea that I need to achieve is to have a simple circle that goes from 0 to 360 degrees.
Inside that circle are two arrows. I need them to rotate inside the circle.
One arrow needs to rotate degree per degree until it reaches a specified angle. The other needs to go directly to that angle.
So your arrows would both start at 0 degrees and if you specified you wanted to go to 100 degrees, one arrow would instantly jump and point towards 100 degrees, while the other would gradualy make it's way to 100 degrees.
EDIT:
Sorry about my lack of skill with stackoverflow (I just realised I never included a question into my question...). So I managed to get simple arrows down in canvas earlier through another question on stackoverflow, but when I started looking into actualy rotating the arrows, I just got lost in the code.
I guess my question was this: how can I apply rotation to my two arrows based on a degree value chosen by the user?
So here's what I managed to make my arrows with:
<html>
<head>
</head>
<body>
<canvas id="c" width="500" height="500"></canvas>
<script langauage="javascript">
<!--
ctx = document.getElementById("c").getContext("2d");
ctx.beginPath();
canvas_arrow(ctx,50,50,100,50);
canvas_arrow(ctx,50,50,10,30);
ctx.stroke();
function canvas_arrow(context, fromx, fromy, tox, toy){
var headlen = 10; // length of head in pixels
var dx = tox-fromx;
var dy = toy-fromy;
var angle = Math.atan2(dy,dx);
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle-Math.PI/6),toy-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(tox, toy);
context.lineTo(tox-headlen*Math.cos(angle+Math.PI/6),toy-headlen*Math.sin(angle+Math.PI/6));
}
-->
</script>
</body>
</head>
The arrows are fine, but getting one of them to rotate while the other one jumps to the desired degree value is what I find hard. I can't find any examples or ideas on how to make them move based on a degree value given by a user.
With jQuery you can get the degrees depending on the mouse position -over your element, and apply the CSS3 transform rotate deg and set the animation transition time:
const $el = $('#circle'),
$cir = $el.children();
$el.on('click', evt => {
const o = $(evt.target).offset(),
rad = $el.width() / 2,
mPos = {
x: evt.pageX - o.left,
y: evt.pageY - o.top
},
a = Math.atan2(mPos.x - rad, mPos.y - rad),
d = -a / (Math.PI / 180) + 180;
$cir.css({transform: `rotate(${d}deg)`});
});
#circle {
position: relative;
width: 150px;
height: 150px;
border-radius: 50%;
font-family: arial, helvetica, sans-serif;
user-select: none; /* prevent text highlight */
cursor: pointer;
}
#circle>* {
text-align: center;
position: absolute;
border-radius: inherit;
width: inherit;
height: inherit;
}
#circle1 {
background: #eee;
transition: 1.3s;
}
#circle2 {
background: #fff;
transition: 0.3s;
top: 20px;
left: 20px;
width: calc(150px - 40px);
height: calc(150px - 40px);
}
<div id="circle">
<div id="circle1">▼</div>
<div id="circle2">▲</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

Center and resize content

I have a user-variable string, which can range from one word to a couple sentences (and might contain any valid Unicode character), which I'd like to display within a variable width box.
In code, I'd like HTML that looks like this w/ any other CSS or JS:
<div style="width: 100%; height: 80%" id="text">
<!--<some more divs or something>-->
{{content}}
<!--</some more divs or something>-->
</div>
{{content}} should get bigger when it can be, up to some maximum font size (variable); smaller when it's longer down to some minimum (variable) and then just get cut off after that point.
In either case, I need it to be visually centered and words longer than the box should get hyphenated.
I've tried hacking something together with a combination of flexboxes and JavaScript, but couldn't figure out how to get all the bugs worked out.
Browser support doesn't really matter aside from the latest versions of mobile/desktop Chrome/Safari/Firefox.
Alright I believe this is what you were wanting to accomplish. Code is below with descriptions in the comment blocks. In chrome you'll be using the -webkit-line-clamp property, in firefox you'll be using the fadeout method since firefox doesn't support the clamp property. You can adjust the fadeout in the css to your liking. The "..." at the cutoff point will also still be present in firefox (see the .clamp:after property in the css).
Here is the updated jsFiddle
HTML (To see the changes, just remove the text until one line is shown in the div)
<div id="textparent">
<div id="text">
{{content}} adkf kfjg; ;akdfg fbfbf egdf hajkh
kajfhdg lakjfg kafd gjkahf jahfkjadlfh alkgj akjdhg fkafg
</div>
</div>
CSS
Note: -webkit-line-clamp:3; ( this is the amount of lines you want to be shown)
#text{
width:100%;
position:relative;
height:auto;
text-overflow:ellipsis;
font-size:25px;
line-height:1.1;
display:block;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow:hidden;
margin:0 auto;
box-sizing:border-box;
}
#textparent{
margin:0 auto;
width:300px;
background:#eee;
top:50px;
padding:10px;
height:auto;
text-align:center;
position:relative;
height:100px;
display:-webkit-box;
-webkit-box-pack:center;
-webkit-box-align:center;
}
/*FIREFOX will make use of the clamp class*/
.clamp:after {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #eeeeee 50%) repeat scroll 0 0 rgba(0, 0, 0, 0);
bottom: 0;
content: "...";
padding: 0 5px 1px 25px;
position: absolute;
right: 0;
}
.clamp {
height: 5.6em;
line-height: 1.4em;
overflow: hidden;
position: relative;
}
Javascript/JQUERY: The main variable you might want to change or play around with is [min_font_size] and [num_line_to_show] although [num_line_to_show] is already set in the CSS.
var t = $('#text');
// get the font-size of the div
var font_size = Number(t.css('font-size').replace('px', ''));
// get the line-height of the div (Note: in Chrome this returns the actual height)
var line_height = Number(t.css('line-height').replace('px', ''));
// minimum height of #text div
//
// Note: if you were in a browser and the line-height var didn't return the full
// height as it does in chrome, you would need to do this:
// var min_h = font-size * line_height
var min_h = line_height;
// number of lines to show. basically just retrieving the "-webkit-line-clamp"
// property in the css, otherwise will default to 3, which you can change.
var num_line_to_show = Number(t.css('-webkit-line-clamp')) || 3;
// the maximum height for the #text div. (the added 5 at the end is just
// personal preference)
var max_h = line_height * num_line_to_show * font_size + 5;
// get the height of the div
var h = $('#text').height();
// set this if you want the font to be set at a minimum size
// when the text is longer than one line
var min_font_size = 20;
Note: you could also try setting the minimum font size dynamically, something like this:
// change this to make the font smaller
var shrink_rate = 3;
var min_font_size = font_size - (Math.round((h/min_h)) * shrink_rate;
Continuing:
// for detecting firefox
var is_ff = navigator.userAgent.toLowerCase().indexOf('firefox');
// if the height of the div is larger than the minimum height, meaning there
// is more than one line now, the font size of the div becomes smaller.
if (h > min_h){
t.css({'font-size' : min_font_size});
// if in Firefox browser
if(is_ff > -1){
// get the new max height of #text based on the number of lines
// with the new minimum font-size
var txt_max_h = ((line_height-font_size) / num_line_to_show) * min_font_size * num_line_to_show;
// the new height is greater than the maximum height allowed for the
// smaller font size
if (t.height() > txt_max_h){
// reset the height of #text div to a fixed height
t.height((min_font_size * num_line_to_show) + 5);
// add the clamp class and css will the rest
t.addClass('clamp');
}
}
}
// if firefox, always run this to center the #text div based on its height
if(is_ff > -1){
t.css({top: ($('#textparent').height() - t.height()) / 2});
}
Hope this helps!
just in time.
See this Fiddle.
I think I succeed to do what you want. It works with Chrome, Firefox and Safari.
HTML :
<div id="container">
<div id="text">my Text !!</div>
</div>
JS :
var maxFontSize=68; // I think we cannot have bigger than that.
var minFontSize=12;
$('#text').on({
// setting an event to resize text
resize:function(e){
// if no text => return false;
if (!$(this).html().trim()) return;
// if already running => return false;
if (this.running) return;
this.running = true;
// get max-height = height of the parent element
var h = $(this).parent().height();
// clone the text element and apply some css
var clone = $(this).clone()
.removeAttr('id')
.css({'font-size':0,
'width':$(this).width(),
'opacity':0,
'position':'fixed',
'left':-1000})
.appendTo($('body'));
// Set the max font size for the clone to fit the max height;
var fontSize = minFontSize;
do {
$(this).css('font-size', fontSize+'px');
fontSize=fontSize+1;
clone.css('font-size', fontSize+'px');
} while (h > clone.height() && maxFontSize > fontSize) ;
// Set the '...' if still bigger
//start by setting back the good size to the clone.
fontSize=fontSize-1;
clone.css('font-size', fontSize+'px');
// while max-height still bigger than clone height
if (h < clone.height() && minFontSize == fontSize) {
var content = clone.html();
// try to remove the last words, one by one.
while (h < clone.height()) {
content = content.replace(/(\s[^\s]*)$/g,'...');
clone.html(content);
}
// then replace the #text content
$(this).html(clone.html());
}
// then remove the clone
clone.remove();
this.running = false;
}
})
.trigger('resize');
There is a cross-browser (IE9+) css centered text and hyphenated for webkit, codepen:
HTML:
<div class="box">
<p>
You can also position your element only in the vertical or horizontal.
This work in IE9+. This text can be also hyphenated.
</p>
</div>
CSS:
.box {
border: #3071a9 solid 1px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
color: #222;
font-size: 26px;
font-family: arial;
height: 50%;
padding: 20px;
width: 50%;
}
.box p {
text-overflow:ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow: hidden;
position: absolute;
top: 50%;
-webkit-transform: translate(0, -50%);
-ms-transform: translate(0, -50%);
-o-transform: translate(0, -50%);
transform: translate(0, -50%);
}
Jquery Textfill Plugin by Russ Painter can come handy.
Here is the Fiddle.
<div>
<div>
<label for="dyntext">Content:</label>
<input type="text" id="dyntext" value="Hello!"></input>
</div>
<div>
<label for="maxsize">Maximal font size in pixels?</label>
<input type="text" id="maxsize" value="0"></input>
</div>
<hr />
<div class="content">
<div class="jtextfill">
<span class="dyntextval">Hello!</span>
</div>
</div>
function update() {
var size = parseInt($('#maxsize').val(), 10);
if (!isNaN(size)) {
$('.dyntextval').html($('#dyntext').val());
$('.jtextfill').textfill({debug: true, maxFontPixels: size});
}
}
$(function () {
$('#maxsize').keyup(update);
$('#dyntext').keyup(update);
update()
});
.content .jtextfill {
width: 150px;
height: 100px;
background-color: #fff;
text-align: center;
border:1px solid #333;
padding-top:40px;
padding-bottom:40px;
}
The center part is really easy, you can do this with flexbox, display:table-cell, etc
The font-size part is tricky but it's been answered in the past here: https://stackoverflow.com/a/6112914/1877754

How to fetch the background of DIV on a bottom layer with exact position using jQuery and CSS

I'm looking to make a page that has a background gradient that changes color every few seconds and blends between transitions. Now I want to apply this effect on the to the upper elements that are blocked by a element that has a solid background.
To give you a better example what I mean I have attached a simple mockup and hopefully your understand what I'm attempting to do, I'm open to suggestions.
(source: bybe.net)
The problem is obviously the block that contains the black background which any PNG transparent used would see black not the gradient.
I'll include sample code so far:
<body><!-- A Jquery script will be used to add CSS background, Easy stuff -->
<div class="blackbox">
<div class="logo"><img src="#" alt=""></div>
<hr class="h-line">
<div class="v-line"> </div>
</div>
So what I'm after is either:
A known jQuery method to obtain a background image but it needs to be able to refer of the position of the gradient so its inline with the background.
A better solution to getting this to work, please bare in mind that the page needs to be responsive so I could use other methods but since its responsive I can't think of any.
Since you ask for alternatives to jQuery solutions
You could play a little with margins and box-shadow and keyframe animations.
Something in this direction for the shape (depends on what you want to do with which part - add content ... and in what way you want it to be responsive):
html:
<div class="wrapper">
<div class="header"><img src="http://i.imgur.com/CUbOIxr.png" alt="Company name" /></div>
<div class="content"></div>
</div>
CSS:
body {
background:orange;
width:100%;
height:100%;
}
.wrapper {
width:40%;
height:90%;
border:30px solid #000;
border-right-width:100px;
border-bottom-width:100px;
}
.header {
width:100%;
border-bottom:10px solid transparent;
-webkit-box-shadow: 0 30px 0 #000;
-moz-box-shadow: 0 30px 0 #000;
box-shadow: 0 30px 0 #000;
}
.header img {
width:100%;
}
.content {
width:95%;
height:400px;
background-color:#000;
margin-top:30px;
}
DEMO
This way no javascript is needed. And for the background you can use a linear gradient and do all animations with css transitions or keyframe animations. You also need to play with the lengths and adjust the borders and box-shadows to your needs, maybe add some #media queries for the responsiveness.
Hope this helps you a little in the right direction =)
Update:
I hoped the gradients changing was the smaller problem ;-) Silly me, sorry.
I will elaborate my CSS-only suggestion for the animation, but you can choose a javascript slider for the background animation, if you don't like CSS3 solutions - although this is the hot stuff now ;-)
Ok. So, I would add some more fixed positioned elements with gradient backgrounds (layer1 and layer2).
To have something in this direction in the html now:
<div class="layer layer1"></div>
<div class="layer layer2"></div>
<div class="wrapper">
<div class="header">
<img src="http://newtpond.com/test/company-name.png" alt="Company name" />
</div>
<div class="content"></div>
</div>
and add a keyframe animation on them in CSS (here it is just with the -webkit vendor prefix [probably cause I am a lazy bum], but I hope you can get the idea, and could add the others):
body {
width:100%;
height:100%;
margin:0;
padding:0;
}
/* for the animation */
.layer {
position:fixed;
width:100%;
height:100%;
}
#-webkit-keyframes GoLayer1 {
0% {
opacity:1;
}
50% {
opacity:0;
}
100% {
opacity:1;
}
}
#-webkit-keyframes GoLayer2 {
0% {
opacity:0;
}
50% {
opacity:1;
}
100% {
opacity:0;
}
}
.layer1 {
background: -webkit-linear-gradient(bottom, rgb(43, 70, 94) 29%, rgb(194, 41, 41) 65%, rgb(155, 171, 38) 83%);
-webkit-animation: GoLayer1 5s infinite;
}
.layer2 {
background: -webkit-linear-gradient(bottom, rgb(225, 202, 230) 29%, rgb(39, 163, 194) 65%, rgb(36, 124, 171) 83%);
-webkit-animation: GoLayer2 5s infinite;
}
/* the wrapper shape */
.wrapper {
z-index:999;
opacity:1;
position:relative;
width:40%;
height:90%;
border:30px solid #000;
border-right-width:100px;
border-bottom-width:100px;
}
.header {
width:100%;
border-bottom:10px solid transparent;
-webkit-box-shadow: 0 30px 0 #000;
-moz-box-shadow: 0 30px 0 #000;
box-shadow: 0 30px 0 #000;
}
.header img {
width:100%;
}
.content {
width:95%;
height:400px;
background-color:#000;
margin-top:28px;
}
DEMO (tested in Chrome 26 - looked cool =)
This is now where I can point you according this CSS-only approach. There is still stuff to modify and consider browser compatibility. But it is certainly an alternative ... and a step in the direction where html5 and css3 is going (if you want to be hot and cool ;-), hehe, sorry, too much silliness.
Good luck!
Update 2:
So, I overcame my laziness a tiny bit and added some more vendor prefixes to the top example (and of course you can use any image as background):
DEMO
And here I add another example, that is using a png image for the gradient, and is sliding up and down in the background (as another alternative):
DEMO
There are many ways to do this, CSS3 and images are already suggested, so I'll suggest using a canvas.
The HTML canvas element has everything you need built in. It allows for gradient background fills, and with globalCompositeOperation, masking of shapes and text is possible, creating cut-outs in the background to make real changeable HTML elements truly transparent against a colored background. It also scales well, and can easily be made responsive.
The canvas element is supported in all major browsers except Internet Explorer 8 and below, which means browser support is better than many of the CSS3 methods previously mentioned, like keyframes and background-size.
Using a fallback, like say images that fade in and out if canvas is'nt available, should'nt be very hard to figure out, and in all other browsers except Internet Explorer below version 9, no images would be needed to create the gradient backgrounds and text masks in a canvas, which should make the loading of the page significantly faster.
To detect wether or not canvas is supported, you can use this convenient function :
function isCanvasSupported(){
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
}
used like so :
if ( isCanvasSupported() ) {
// do canvas stuff
}else{
// fall back to images
}
So, lets get to it! To create a "last resort" fallback and some elements we can "clone" into the canvas, we'll create the elements we need in the HTML to get a structure somewhat similar to what you've outlined in your question. This has the added advantage of being able to just change some of the CSS to also make changes in the canvas :
<div id="gradient">
<div class="text">COMPANY NAME</div>
<div class="h_bar"></div>
<div class="v_bar"></div>
</div>
It's just a container with an element for text, and one for each of the bars.
Some styling is neccessary as well, I'll do it the easy way, with position absolute and some really fast positioning, as these elements won't be visible unless someone has disabled javascript anyway :
#gradient {position: absolute;
background: #000;
top: 5%; left: 5%; right: 5%; bottom: 5%;
}
.text {position: absolute;
top: 20px;
left: 100px;
width: 400px;
color: #fff; font-size: 40px; font-weight: bold;
font-family: arial, verdana, sans-serif;
}
.h_bar {position: absolute;
height: 20px;
top: 100px; left: 60px; right: 60px;
background: #fff;
}
.v_bar {position: absolute;
width: 20px;
top: 140px; bottom: 30px; right: 60px;
background: #fff;
}
Without any javascript that would look exactly like THIS FIDDLE, and it should be somewhat responsive and adapt to the window size.
Now we need some javascript to turn those elements into something in a canvas. We'll create two canvas elements, one for the background, as I've decided to animate the background continously between random gradients, and one for the inner black box and the content (the text and the bars).
As the masking of the text and bars can be a little slow, we don't have to redraw everything, just the background canvas, as the foreground is pretty static.
This also avoids a flickering issue in some browsers with high frame rates, and we're going to use requestAnimationFrame for the animation of the background canvas, so flickering in the text mask would be an issue if we did'nt use two canvas elements.
For browsers that does'nt support requestAnimationFrame we'll add this polyfill to make sure it works everywhere.
Time to write some javascript, this of course uses jQuery :
var gradSite = {
init: function() {
var self = this;
self.create().setSizes().events();
(function animationloop(){
requestAnimationFrame(animationloop);
self.draw().colors.generate();
})();
},
create: function() { // creates the canvas elements
this.canvas = document.createElement('canvas');
this.canvas2 = document.createElement('canvas');
this.canvas.id = 'canvas1';
this.canvas2.id = 'canvas2';
this.canvas.style.position = 'absolute';
this.canvas2.style.position = 'absolute';
$('#gradient').after(this.canvas, this.canvas2);
return this;
},
events: function() { //event handlers
$(window).on('resize', this.setSizes);
$('#gradient').on('contentchange', this.draw2);
return this;
},
setSizes: function() { // sets sizes on load and resize
var self = gradSite,
w = $(window),
m = $('#gradient');
self.canvas.height = w.height();
self.canvas.width = w.width();
self.canvas2.bg = m.css('background-color');
self.canvas2.height = m.height();
self.canvas2.width = m.width();
self.canvas2.style.top = m.offset().top + 'px';
self.canvas2.style.left = m.offset().left + 'px';
self.draw2();
return self;
},
colors: {
colors: {
0: [255,255,0],
1: [255,170,0],
2: [255,0,0]
},
map: {
0: [0,0,1],
1: [0,1,1],
2: [0,1,1]
},
generate: function() { // generates the random colors
var self = this;
$.each(self.colors, function(i,color) {
$.each(color, function(j, c) {
var r = Math.random(),
r2 = Math.random(),
val = self.map[i][j] == 0 ? (c-(j+r)) : (c+(j+r2));
if (c > 255) self.map[i][j] = 0;
if (c < 0 ) self.map[i][j] = 1;
self.colors[i][j] = val;
});
});
}
},
raf: (function() { // polyfill for requestAnimationFrame
var lastTime = 0,
vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime(),
timeToCall = Math.max(0, 16 - (currTime - lastTime)),
id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}()),
calculateColor: function(colors) { // returns a rgb color from the array
return 'rgb(' + Math.round(colors[0]) + ',' + Math.round(colors[1]) + ',' + Math.round(colors[2]) + ')';
},
draw: function() { //draws the color background
var self = this,
c = self.canvas || document.getElementById('canvas1'),
ctx = c.getContext('2d'),
grad = ctx.createLinearGradient(0,0,0,self.canvas.height);
c.width = c.width;
grad.addColorStop(0, self.calculateColor(self.colors.colors[0]));
grad.addColorStop(0.5, self.calculateColor(self.colors.colors[1]));
grad.addColorStop(1, self.calculateColor(self.colors.colors[2]));
ctx.fillStyle = grad;
ctx.fillRect(0,0,self.canvas.width, self.canvas.height);
return self;
},
draw2: function() { // draws the black square and content
var self = this,
c = self.canvas2 || document.getElementById('canvas2'),
ctx2 = c.getContext('2d'),
txt = $('.text', '#gradient').first(),
hbar = $('.h_bar', '#gradient').first(),
vbar = $('.v_bar', '#gradient').first();
c.width = c.width;
ctx2.globalCompositeOperation = 'xor';
ctx2.font = txt.css('font');
ctx2.fillStyle = c.bg || '#000';
ctx2.fillText(txt.text(), txt.offset().left, txt.offset().top);
ctx2.fillRect(hbar.position().left, hbar.position().top, hbar.width(),hbar.height());
ctx2.fillRect(vbar.position().left, vbar.position().top, vbar.width(),vbar.height());
ctx2.fillRect(0,0,c.width,c.height);
}
}
The raf function would be the polyfill for requestAnimationFrame, and the two draw functions create the content in the canvas. It's really not that complicated.
We will call the above script inside a DOM ready handler, like so :
$(function() {
gradSite.init(); // starts the canvas stuff
});
Adding all that up into a fiddle, and adding a few elements for demonstration purposes, it would look like THIS FIDDLE, and here's the finished ->
FULL SCREEN DEMO
The only way I can see this working is if your black div has no background and is cut into sections that that each have a background. The company name area would need to have the same foreground color as the background for the rest of the div sections. Depending on your layout needs this might be fine.
For example, you could cut it into three sections and two images:
You can try combinig background-size and background-position with javascript:
setGradientSizes = function (el) {
var width = $(document).width() + 'px', height = $(document).height() + 'px';
$(el || '.gradient:not(body)').each(function () {
var offset = $(this).offset();
$(this).css('background-size', width + ' ' + height);
$(this).css('background-position', (offset.left * -1) + 'px ' + (offset.top * -1) + 'px');
})};
Working example here -> jsbin
NOTES:
this is not 100% cross browser - background-size is supported in FF4.0+, IE9.0+, Opera 10.0+, Chrome 1.0+, Safari 3+.
For some older browsers you can try browser specific prefixes (like -moz-background-size) - my example does not cover that.
To reduce load flickering you can apply calculations at first and then add background gradient
You could make the background of the image with the text black, then set the div's background color to rgba(0,0,0,0) making it transparent
This might be helpful for you according to my understanding
There is inherit to copy a certain value from a parent to its children, but there is no property the other way round (which would involve another selector to decide which style to revert).
You will have to revert style changes manually:
div { color: green; }
form div { color: red; }
form div div.content { color: green; }
If you have access to the markup, you can add several classes to style precisely what you need:
form div.sub { color: red; }
form div div.content { /* remains green */ }
Edit: The CSS Working Group is up to something:
div.content {
all: default;
}
If I was you I'll duplicate the css and jQuery, print it on a div on top of what ever and make the overflow hidden (like masking layers but with z-index).

Categories

Resources