How to use WebGL to render a button background? - html

Let's say that I have a GL shader I'd want to use as an HTML button background (it's a pretty animated gradient). How can I achieve that?
I can think of setting the background to a texture and rendering the shader to this texture. Is that fundamentally possible?

Using a canvas inside a button is the most logical way to go, note that this is just an example and not a shader, but we use the canvas to render WebGL so it can be slightly different for your case.
var c = document.getElementsByTagName("canvas")[0];
var ctx = c.getContext("2d");
// Create gradient
var grd = ctx.createLinearGradient(0, 0, 200, 0);
grd.addColorStop(0, "red");
grd.addColorStop(1, "green");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 400, 200);
button {
position: relative;
background-color: transparent;
border: none;
outline: none;
padding: 8px;
}
button > div {
position: relative;
z-index: 1;
}
canvas {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
}
<button>
<canvas></canvas>
<div>Fancy Button</div>
</button>

Related

How i can create curved 3d objects with html and css

I have an html/css 3d object as in picture 2. I want to make it curve a little bit as in picture 1
Does someone have an idea perhaps how it could be achieved Or if there is any other technology by which I can shape these 3d objects ?
This is the code for my object in the picture 2:
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.cube {
background: #dc2e2e;
width: 200px;
height: 50px;
position: relative;
margin: 50px;
transform: rotate(32deg);
}
.cube::before {
content: '';
display: inline-block;
background: #f15757;
width: 200px;
height: 2px;
transform: skewX( -80deg);
position: absolute;
top: -2px;
left: 6px;
}
.cube::after {
content: '';
display: inline-block;
background: #9e1515;
width: 12px;
height: 50px;
transform: skewY(-10deg);
position: absolute;
top: -1px;
left: 100%;
}
<div class="container">
<div class="cube"></div>
</div>
Creating an arc out of a div element is not so straightforward . Also using div tag to create shapes and graphics is not a good way instead you should use Scalable Vector Graphics(SVG), HTML Canvas, WebGL or any other JS libraries.
Using div tag you can create a bottom arc by adding bottom border radius:
.cube {
background: #dc2e2e;
width: 200px;
height: 50px;
position: relative;
margin: 50px;
box-shadow: 0px 10px 16px -6px black;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
<div class="container">
<div class="cube"></div>
</div>
But you can't achieve a top arc using the border top radius because using it will create an arc in opposite direction and thus create an oval instead of an arch.
One thing you can do is to overlay the upper part of the rectangle with another div tag of white color to create an arch like effect.
.arch {
width: 200px;
overflow: hidden;
}
.lowerarc {
width: 240px;
position: relative;
right: -10px;
left: -20px;
height: 80px;
background-color: black;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
.upperarc {
height: 80px;
position: relative;
width: 240px;
top: -120px;
right: -10px;
left: -20px;
background-color: white;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
<div class="arch">
<div class="lowerarc"></div>
<div class="upperarc"></div>
</div>
But it doesn't look good. so using div is probably not the best way to get the desired result.
Try using SVG instead:
In this example below we create an arch using lines and curves by plotting pixel values and filling the shape with a specific color.
M means move to, L to create a line, Q to draw curves, and z to close the path and the number corresponding to it are the values in pixel
<svg width="200">
<path d="M0,0 L0,50 Q100,80 200,50 L200,0 Q100,25 0, 0z" fill="black" />
</svg>
In this code below, we just create a curve line but with a 50px thickness.
<svg viewBox="0 0 1000 400">
<path d="M 60,250 C 60,150 150,50 250,50" fill="none"
stroke="green" stroke-width="50"></path>
</svg>
Use Canvas:
Canvas is similar to Svg but it uses javascript to create path and graphics.
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');
c.beginPath();
c.moveTo(60, 0);
c.lineTo(10, 0);
c.quadraticCurveTo(0, 170, 170, 250);
c.lineTo(195, 210);
c.quadraticCurveTo(50, 150, 60, 0);
c.fill();
<canvas id="canvas" width="622" height="1080"></canvas>
While SVG and Canvas are used to create 2D graphics only,but still you can use 2D objects together to create a 3D like illusion.
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');
c.fillStyle="rgba(0, 0, 25, 0.7)";
c.beginPath();
c.moveTo(10, 0);
c.lineTo(0, 5);
c.quadraticCurveTo(-10, 180, 170, 260);
c.lineTo(195, 220);
c.lineTo(195, 210);
c.quadraticCurveTo(30, 250, 60, 0);
c.fill();
c.fillStyle="rgb(200, 210, 200)";
c.beginPath();
c.moveTo(60, 0);
c.lineTo(10, 0);
c.quadraticCurveTo(0, 165, 170, 250);
c.lineTo(195, 210);
c.quadraticCurveTo(50, 135, 60, 0);
c.fill();
<canvas id="canvas" width="622" height="1080"></canvas>
<script src="main.js">
</script>
SVG and Canvas codes looks scary but it is not as difficult as it looks like. You can read documentations or watch Youtube tutorials to learn it properly.
Making curved 3d objects in pure html/css is not very easy but it is doable. There a few techniques to do it but you will need to understand exactly what you want and how to alter the number to achieve it.
You can stack multiple div elements to create a curved 3d effect and then offset the z axis for each one. Essentially you are just stacking lots of elements together to give the illusion of a curved 3d object. In the below links I use radial gradient.
I would recommend using a pre-proccessor like SCSS for this as it allows you to run a loop to offset each element cutting out a lot of code you would have to write for each element. Also keep in mind if you intend to animate this after then that will be very taxing on the processing depending on device. If you are trying to make it static then it shouldn't be an issue.
SCSS
#for $i from 1 through 20 {
$color: red;
&:nth-child(#{$i}) {
position: absolute;
width: $width;
height: $height;
list-style: none;
background: rgb(0, 0, 0);
background: radial-gradient(
circle at 50% 150%,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) 60%,
darkred 60%,
darkred 70%,
rgba(0, 0, 0, 0) 70%,
rgba(0, 0, 0, 0) 100%
);
transform: translateZ(-20px + (0.8px * ($i - 1)));
filter: blur(0px) contrast(200%);
}
simple curves example 1 https://codepen.io/jfirestorm44/pen/MWmKraz?editors=0100
complex art example 2 https://codepen.io/jfirestorm44/pen/qBZYQBL
Like I said about performance; Example 2 will take some processing power and may lag. The object can be rotate by clicking.
I'd like to add to #Miran Firdausi's answer about SVGs (but I can't comment yet). A good way to create SVGs and then "hard code" them into CSS is to use any vector paint program (such as Inkscape) to draw exactly what you want, then save as SVG.
You can open the .svg file in your code editor and you will see XML markup, including codes which you can copy into your HTML, like in #Miran's example:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" version="1.1" role="img" aria-hidden="true" focusable="false">
[copy the <path> elements here]
</svg>
Or save the .svg file somewhere and refer to it in your CSS:
/* Add calendar icon */
.wp-social-link.my-calendar-icon > a {
background-image: url(assets/images/my-calendar.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 36px;
width: 54px;
height: 54px;
filter: invert(100%) brightness(100%) contrast(100%);
}

CANVAS - line with 100% of width

I would like to draw a line using CANVAS with 100% width acrossing through the entire screen similar to a css in the 'image-background' of the body like this example below:
Ex:
Made with CSS
body {
background-image: url(horizontal-line.png);
background-repeat: repeat-x;
background-position: 0px 10%;
}
made with CANVAS
???
How can I solve it? Thank you
Updated demo:
https://jsfiddle.net/mulperi/xnob50yd/1/
A full window sized canvas and 100% stroke in the middle of the screen
Here are the css and js parts:
<style>
canvas {
padding: 0;
margin: auto;
width: 100%;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>
<canvas id="canvas"></canvas>
<script>
const c = document.getElementById("canvas");
const ctx = c.getContext("2d");
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.fillStyle = "red";
ctx.fillRect(0, window.innerHeight / 2, window.innerWidth, 10);
ctx.fill();
window.onresize = function() {
ctx.width = window.innerWidth;
ctx.height = window.innerHeight;
}
</script>
With window.onresize you make sure that canvas changes size dynamically with the browser window.
Canvas &lthr&gt line
Used javascript to find the width of the screen. document.documentElement.clientWidth
Then used that to set the size of the canvas element. canvas.width = screenWidth;
Then draw a rect width size 2px height and its width equal to screenWidth.
ctx.fillRect(0, 10, screenWidth, 2)
What is the 10 for? In this example i set the canvas to 20 height. So the middle of the canvas is 10.
document.addEventListener("DOMContentLoaded", function() {
draw();
});
function draw() {
var canvas = document.getElementById('demo');
//Set the canvas to viewport size
var screenWidth = document.documentElement.clientWidth;
canvas.width = screenWidth;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(255, 100, 0)';
ctx.fillRect(0, 10, screenWidth, 2);
} else {
convas.addChild("No canvas here");
}
}
body {
margin: 0;
}
/*to make the canvas visible*/
#demo {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
<p>Canvas</p>
<canvas id="demo" width="" height="20"></canvas>

Fading between sprite images without calling out image url in CSS

Is it possible to fade between sprite images on hover without calling out the image URL in the CSS?
I have a ton of two-image sprite sheets. I want each one to switch on hover, but the CSS will be far too bloated if I have to create a new element for every one with a "background: url(x)".
.frame {
height: 500px;
width: 500px;
overflow: hidden;
}
.sprite {
background: url(image.png) 0px 0px no-repeat;
position: relative;
height: 500px;
width: 500px;
}
.sprite::after {
content: "";
background: url(image.png) -500px 0px no-repeat;
opacity: 0;
transition: opacity 0.35s;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.sprite:hover::after {
opacity: 1;
transition: opacity: 0.35s;
}
I'd rather call them out here:
<div class="sprite frame">
</div>
Here's a JSFiddle of the effect I want, but I want to call out the image URL in the HTML, so I don't have 100 different CSS elements calling out different images.
Hmm. If you are open to a jQuery solution this may help. You can use a "data alt-src" HTML attribute to store two image URLs within one tag, then call a jQuery .hover() function on that class.
HTML:
<img class="xyz" data-alt src="http://cdn1.iconfinder.com/data/icons/fatcow/32/accept.png" src="http://cdn1.iconfinder.com/data/icons/fatcow/32/cancel.png" />
jQuery:
var sourceSwap = function () {
var $this = $(this);
var newSource = $this.data('alt-src');
$this.data('alt-src', $this.attr('src'));
$this.attr('src', newSource);
}
$(function () {
$('img.xyz').hover(sourceSwap, sourceSwap);
});
http://jsfiddle.net/LmVRZ/2/
I realize this demo doesn't include the opacity transition but I think it could be built in.

CSS - how to create circle pie canvas like this?

I really like this element,
but how to create it? I am not sure what's the correct designation of the element...
Thank you very much.
This effect can be achieved by layering a couple arc()s:
// bright blue full circle
d.beginPath();
d.arc(50, 50, 50, 0, 2 * Math.PI, false);
d.fillStyle = "#aaeeff";
d.fill();
// dark blue percentage circle
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 50, -0.5 * Math.PI, 0.78 * 2 * Math.PI - 0.5 * Math.PI, false);
d.fillStyle = "#00aaff";
d.fill();
// white inner filler
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 25, 0, 2 * Math.PI, false);
d.fillStyle = "#ffffff";
d.fill();
and finally rendering the text:
d.moveTo(50, 50);
d.fillStyle = "#606060";
d.font = "12pt sans-serif";
d.fillText("78%", 36, 56);
Fiddle: http://jsfiddle.net/j6NVg/
Instead of using the <canvas> element, I have chosen to construct the pie chart relying on CSS and JS entirely. The HTML markup is as follow:
<div class="pie" data-percent="78">
<div class="left">
<span></span>
</div>
<div class="right">
<span></span>
</div>
</div>
The CSS is as follow. The trick is to split the circle into two halves (the nested .left and .right elements). The halves will have their overflowing content hidden, and contain nested <span> that we will manipulate with JS for rotation later. Add vendor prefixes when appropriate :)
.pie {
background-color: #eee;
border-radius: 50%;
width: 200px;
height: 200px;
overflow: hidden;
position: relative;
}
.pie > div {
float: left;
width: 50%;
height: 100%;
position: relative;
overflow: hidden;
}
.pie span {
background-color: #4a7298;
display: block;
width: 100%;
height: 100%;
}
.pie .left span {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
-webkit-transform-origin: 100% 50%;
transform-origin: 100% 50%;
}
.pie .right span {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
-webkit-transform-origin: 0% 50%;
transform-origin: 0% 50%;
}
.pie:before,
.pie:after {
border-radius: 50%;
display: block;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
.pie:before {
background-color: #fff;
content: "";
width: 75%;
height: 75%;
z-index: 100;
}
.pie:after {
content: attr(data-percent) "%";
z-index: 200;
text-align: center;
}
I have used the following with jQuery:
$(function() {
$(".pie").each(function() {
var percent = $(this).data("percent").slice(0,-1), // Removes '%'
$left = $(this).find(".left span"),
$right = $(this).find(".right span"),
deg;
if(percent<=50) {
// Hide left
$left.hide();
// Adjust right
deg = 180 - (percent/100*360)
$right.css({
"transform": "rotateZ(-"+deg+"deg)"
});
} else {
// Adjust left
deg = 180 - ((percent-50)/100*360)
$left.css({
"transform": "rotateZ(-"+deg+"deg)"
});
}
});
});
Here is the fiddle: http://jsfiddle.net/Aw5Rf/7/
Check the below links for more info (not an exact one.But you can get some idea).
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Test</title>
</head>
<body>
<section>
<div>
<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<script type="text/javascript">
var myColor = ["#ECD078","#D95B43","#C02942","#542437","#53777A"];
var myData = [10,30,20,60,40];
function getTotal(){
var myTotal = 0;
for (var j = 0; j < myData.length; j++) {
myTotal += (typeof myData[j] == 'number') ? myData[j] : 0;
}
return myTotal;
}
function plotData() {
var canvas;
var ctx;
var lastend = 0;
var myTotal = getTotal();
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < myData.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(200,150);
ctx.arc(200,150,150,lastend,lastend+
(Math.PI*2*(myData[i]/myTotal)),false);
ctx.lineTo(200,150);
ctx.fill();
lastend += Math.PI*2*(myData[i]/myTotal);
}
}
plotData();
</script>
</section>
</body>
</html>
For more info :Graphing Data in the HTML5 Canvas Element Simple Pie Charts
Another Link : Pure CSS3 Pie Charts effect
This is an online demo: http://jsbin.com/uFaSOwO/1/
First of all what you need can be done exactly using jQuery knob plugin. Still interested in a CSS Solution, than here's what I have done
<div class="load_me"></div>
.load_me {
margin: 100px;
height: 50px;
width: 50px;
border: 5px solid #f00;
border-radius: 50%;
border-top-color: transparent;
}
Demo
Animating the Knob Credits
If you want to prevent the mouse alteration, you can simply add readOnly
$this.knob({
readOnly: true
});
Demo
FIDDLE with ANIMATION
Here's my approach:
var ctx = canvas.getContext('2d');
/*
* in canvas, 0 degrees angle is on the right edge of a circle,
* while we want to start at the top edge of the circle.
* We'll use this variable to compensate the difference.
*/
var relativeAngle = 270;
function drawCanvas() {
ctx.clearRect(0, 0, 90, 90);
//light blue circle
ctx.lineWidth = 20;
ctx.strokeStyle = '#D8E8F7';
ctx.beginPath();
ctx.arc(45, 45, 35, 0, 2*Math.PI);
ctx.stroke();
//dark blue circle
ctx.strokeStyle = '#66ADF4';
ctx.beginPath();
//notice the angle conversion from degrees to radians in the 5th argument
ctx.arc(45, 45, 35, 1.5*Math.PI, ((angle + relativeAngle) / 180) * Math.PI);
ctx.stroke();
//text
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillStyle = '#666';
ctx.font = 'bold 14px serif';
// angle conversion to percentage value
ctx.fillText(parseInt(100 * angle / 360).toString() + '%', 45, 45);
}
var angle;
function timeout() {
angle = parseInt(360 * percent / 100);
drawCanvas();
if (angle > 360) {
document.getElementById('run').disabled = false;
percent = 0;
return;
}
percent++;
setTimeout(timeout, 10);
};
var percent = 0;
/* START the ANIMATION */
timeout();
At the bottom of the code you'll find a self evaluating function timeout which calls the drawCanvas function every 10 miliseconds and increments the blue circle angle. I hope everything is clear here. If not, feel free to ask!
Enjoy it!

Creating a transparent inner notch?

I know how to create a notch on the outside like:
div:after {
content: '';
display: block;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
But I can't figure out how to solve this thingy using CSS only:
The notch has to be inside of the container and it has to be transparent. So the above solution or an image won't solve it.
Maybe this can be created using SVG?
Edit
What I tried is this:
body {
background: #eee;
}
div {
position: relative;
height: 100px;
width: 200px;
background: #ccc;
}
div:after {
content: '';
position: absolute;
display: block;
top: 40px;
right: -10px;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: #eee;
}
But this is clearly no soultion, because the pseudo element is not tranparent.
You cannot do this with pure CSS as clipping is not fully supported yet in all browsers (if cross-compatibility is important).
You would need to combine SVG clipping paths with CSS clipping and would end up with a not so elegant solution.
What you can do however is to create a background image using canvas. Canvas is supported in all the major HTML5 capable browsers. The backdraw with canvas is that you need to do a little more coding to create the shape. Optional an image could have been used instead but using canvas allow you to keep everything sharp (and not blurry as with an image when it is stretched).
The following solution will produce this result (I added red border to show the transparent region). You can tweak the parameters to get it look exactly as you need it to look and extend it with arguments to define size of the notch, width of transparent area etc. The code automatically adopts to the size of the element given as argument.
To add a notch simply call:
addNotch(element);
ONLINE DEMO HERE
The code is straight-forward and performs fast:
function addNotch(element) {
/// some setup
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
/// get size of element in pixels
cs = window.getComputedStyle(element),
w = parseInt(cs.getPropertyValue('width') || '0', 10),
h = parseInt(cs.getPropertyValue('height') || '0', 10),
/// pre-calculate some values
hh = h * 0.5,
nw = 20, /// notch size
nh = nw * 0.5;
canvas.width = w;
canvas.height = h;
/// draw the main shape
ctx.moveTo(0, 0);
ctx.lineTo(w - nw, 0);
ctx.lineTo(w - nw, hh - nh);
ctx.lineTo(w - nw - nh, hh);
ctx.lineTo(w - nw, hh + nh);
ctx.lineTo(w - nw, h);
ctx.lineTo(0, h);
ctx.closePath();
ctx.fillStyle = '#7c7058';
ctx.fill();
/// draw the white arrow
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#eee';
ctx.moveTo(w - nw - nw * 0.33, hh - nw * 0.75);
ctx.lineTo(w - nw - nw * 1.1, hh);
ctx.lineTo(w - nw - nw * 0.33, hh + nw * 0.75);
ctx.stroke();
/// convert canvas to image and set background of element
/// with this image
element.style.background = 'url(' + canvas.toDataURL() +
') no-repeat left top';
}
Here's an example using SVG clipping.
jsFiddle Demo
<div></div>
<svg>
<defs>
<clipPath id="clipping">
<polygon points="
0 0, 202 0,
202 36, 185 50, 202 64,
202 102, 0 102" />
</clipPath>
</defs>
</svg>
Try this fiddle out, it should set you on your way for what you're looking for.
#notched {
width: 0px;
height: 0px;
border-right: 60px solid transparent;
border-top: 60px solid #d35400;
border-left: 60px solid #d35400;
border-bottom: 60px solid #d35400;
}
Updated fiddle
You can use the :before for mask and after selector for the border, just set border-lef and border-bottom property.
div:before {
content: '';
position: absolute;
display: block;
top: 40px;
right: -10px;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: #eee;
}
div:after {
content: '';
position: absolute;
display: block;
top: 38px;
right: -5px;
width: 20px;
height: 21px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: transparent;
border-left: 2px solid #eee;
border-bottom: 2px solid #eee;
}
the result:
jsFiddle