I have a basic HTML Slider element. What I wish to produce is simple - when the slider is at one end a simple happy face is output. When it is at the other end, a simple sad face is output.
The key thing I want is that there will be a smooth transition from one side of the animation to the other - so the smiley face will go through different phases of indifference and slight happiness/slight sadness.
Let's say there is a float of between 1 and 10 output from the slider, which corresponds to the displayed image's "happiness".
How would you tackle this problem? I have tried searching and googling, but with no good results. All technologies acceptable - particularly interested in how the image/animation would be stored.
I have a basic HTML Slider element. What I wish to produce is simple - when the slider is at one end a simple happy face is output. When it is at the other end, a simple sad face is output.
The key thing I want is that there will be a smooth transition from one side of the animation to the other - so the smiley face will go through different phases of indifference and slight happiness/slight sadness.
Let's say there is a float of between 1 and 10 output from the slider, which corresponds to the displayed image's "happiness".
How would you tackle this problem? I have tried searching and googling, but with no good results. All technologies acceptable - particularly interested in how the image/animation would be stored.
Share Improve this question asked Oct 1, 2013 at 20:46 Simon KielySimon Kiely 6,05029 gold badges100 silver badges191 bronze badges 1- 1 You could do something like Akismet did for their Personal plan's "What is akismet worth to you?" slider at akismet.com/account/upgrade. They just have an image of the different smiley faces, and switch between them akismet.com/img/ab/smiley.png – Joshua Dwire Commented Oct 1, 2013 at 22:38
5 Answers
Reset to default 15 +350You could use CSS's transform: rotateX
to do what you'd want. In my version I also used HTML5's input
with type="range"
, but if you wanted to affect old browsers you could use the same approach with a more global slider. The demo involves using two images, one for the face and background and one for the lips, but if you like the technique you can apply it to pure CSS the same way. Just make sure you include all browser prefixes you want in the javascript
Live Demo Here
/* HTML */
<div id='slimey'>
<div id='lips'></div>
</div>
<input id="smileSlide" type="range" onchange="changeSmile(this)" value="0" />
/* Javascript */
var lips = document.getElementById('lips');
function changeSmile(slider) {
var sliderVal = slider.value,
rotateDegree = - sliderVal * 1.8;
lips.style.webkitTransform = "rotateX(" + rotateDegree + "deg)";
lips.style.transform = "rotateX(" + rotateDegree + "deg)";
}
/* CSS */
#slimey {
margin:auto;
background:url(http://i.imgur.com/LGQMhc3.jpg);
background-size: 100% 100%;
height:100px;
width:100px;
position:relative;
}
#lips {
width:50px;
height:20px;
position:absolute;
top:calc(70% - 10px);
left:calc(50% - 25px);
background:url(http://i.imgur.com/20EmUM7.gif);
background-size: 100% 100%;
-webkit-transform:rotateX(0deg);
transform:rotateX(0deg);
}
#smileSlide {
width:100px;
position:absolute;
left:calc(50% - 50px);
}
Inspired by Marco Barria's (this guy has some awesome projects) single element flying bird
If you wanted to make the middle state more visible, you could toggle the display of a line in the middle range like this demo does. Like the rest of this answer it's only an approximate solution, but I think it shows the technique well. If you wanted to get super fancy you could even add a fade in/out for the line to make it appear a tiny bit smoother
I have changed Zeaklous answer by using pure css and js (without any images or rotate) http://jsfiddle.net/sijav/PVvc4/3/
<!--HTML-->
<div id='slimey'>
<div id='leye' class='eye'></div>
<div id='reye' class='eye'></div>
<div id='lips'></div>
</div>
<input id="smileSlide" type="range" onchange="changeSmile(this)" value="0" />
//Java Script
var lips = document.getElementById('lips');
function changeSmile(slider) {
var lh = lips.style.height, slide=0;
if ((50 - slider.value) > 0){
slide = (50 - slider.value);
lips.style.borderTop = "2px black solid";
lips.style.borderBottom = "none";
}
else {
slide = (slider.value - 50);
lips.style.borderBottom = "2px black solid";
lips.style.borderTop = "none";
}
lips.style.height = slide * 0.4 + "px" ;
lips.style.top = "calc(70% + " + (- slide) * 0.2 + "px" ;
}
/**CSS**/
#leye{
left:25%;
}
#reye{
right:25%;
}
.eye{
position:absolute;
background:black;
border-radius:100%;
height:15px;
width:15px;
top:20px;
}
#slimey {
margin:auto;
background:Yellow;
border-radius:100%;
height:100px;
width:100px;
position:relative;
}
#lips {
width:50px;
height:20px;
position:absolute;
top:calc(70% - 10px);
left:calc(50% - 25px);
background:transparent;
border-top:2px black solid;
-webkit-transform:rotateX(0deg);
border-radius:100%;
transform:rotateX(0deg);
}
#smileSlide {
width:100px;
position:absolute;
left:calc(50% - 50px);
}
EDIT: a small difference in lips moving here http://jsfiddle.net/sijav/PVvc4/4/
For fun, I hacked up a little Webkit-only version in pure CSS (no images or JavaScript) over lunch—here's a demo. I wouldn't necessarily recommend this approach, but I couldn't resist giving it a whirl!
It's a pretty simple approach. Instead of a slider I used a group of radio buttons, which allows you to use the :checked
pseudo-class to change the colours and shape of the mouth depending on the selected value. The mouth shape is described using just height
and border-radius
. For example:
#feel-0:checked ~ #mouth {
border-radius: 30px 30px 0 0;
height: 20px;
margin-top: -5px;
}
The explanatory text is just the label field for the selected radio button. Initially all labels are hidden, but once a field is checked the adjacent label becomes visible:
#happiness input:checked + label {
visibility: visible;
}
You can change which field is selected (visually moving the slider left or right) using your arrow keys—this is built-in behaviour for a radio group.
With a little work, you could adapt this to make the slider look better in non-WebKit browsers; however, it may be janky in some older browsers, and you can't drag the slider left or right. For production I would use a JavaScript slider (e.g. one of the many jQuery options available) and swap the CSS pseudo-class magic for a smattering of JS and a handful of classes.
You could use something like Raphael if you can draw the face in SVG. If you were to keep the same circle face and just morph the mouth for example. This would animate between the shapes. Not quite sure if thats what you mean. This is my 2 second terrible mouth path.
var paper = Raphael( "canvas_container",400,400);
paper.circle(280, 210, 150);
// create the 2 paths to animate between in some app like inkscape
var pathStr1 = "m385,255l-197,-3l40,57l88,1l35,0l34,-55z",
pathStr2 = "m207,268l162,-1l-33,-45l-41,-2l-46,-1l-32,20l-10,29z";
var path = paper.path(pathStr1).attr({fill: "yellow", "stroke-width": 3});
setTimeout(function(){path.animate({path: pathStr2}, 3000, "linear");}, 1000);
There's a fiddle at http://jsfiddle.net/YUhHw/1/ and tutorial with better examples (code above based part on) at http://cancerbero.mbarreneche.com/raphaeltut/#sec-animation and nice examples at http://raphaeljs.com/animation.html I guess you would need to change the setTimeout to correspond to your slider.
Just use a png face without a mouth, and several tween mouths that switch based on the location along the slider with Javascript. Store the files in an array, and call to them from a timed function to check the faces position. Simple and lightweight.
var mouthArray = new Array('frown','half_frown','half_smile','smile');
var face = document.getElementById('face');
var faceLoc = parseInt(face.style.left);
function changeMouth() {
if(faceLoc<10)
{theMouth.setAttribute('src',mouthArray[0]);}
if(faceLoc>10,faceLoc<=19)
{theMouth.setAttribute('src',mouthArray[1]);}
if(faceLoc>20,faceLoc<=29)
{theMouth.setAttribute('src',mouthArray[2]);}
if(faceLoc>30,faceLoc<40)
{theMouth.setAttribute('src',mouthArray[3]);}
}
Something along those lines, if given enough iterations for the length of the slider, should get you a decently 'smooth' effect.