Как сделать смайлик, у которого улыбка сменяется на грусть при клике
помогите с определением координат x,y для анимации улыбки и грусти с помощью canvas и requestAnimationFrame
нужно анимировать рот чтобы он из улыбки превращался в грусть
window.onload = () => {
const c = document.getElementById('canvas').getContext('2d');
c.lineWidth = 4;
c.strokeStyle = '#ff0000';
let r = 0.01, dr = -0.01;
const draw = () => {
c.clearRect(0, 0, 400, 400);
c.beginPath();
c.arc(200, 200, 100, 0, Math.PI * 2, true); // лицо
c.moveTo(270, 200);
c.arc(200, 200, 70, 0, Math.PI, false); // рот
c.moveTo(185, 190);
c.arc(175, 190, 10, 0, Math.PI * 2, true); // левый глаз
c.moveTo(235, 190);
c.arc(225, 190, 10, 0, Math.PI * 2, true); // правый глаз
r += dr;
if (r <= 0.001 || r >= 0.2) dr = -dr;
c.stroke();
window.requestAnimationFrame(draw);
}
draw();
}
body {
margin: 0;
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: #ffffff;
}
canvas {
background: #474a51;
width: 200px;
height: 200px;
border: 1px solid #494d4e;
border-radius: 4px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
</body>
</html>
Ответы (2 шт):
нужно анимировать рот чтобы он из улыбки превращался в грусть
Нужно создать два смайлика:
- Улыбка, радость - уголки губ вверх
- Печаль - уголки губ вниз
Можно взять готовый, улыбающийся смайлик, их в сети миллионы, и в векторном редакторе опустить уголки губ. Если квалификация позволяет можно написать код в ручную.
Я покажу технику с помощью векторного редактора
Исходный смайлик:
<svg id="svg1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" width="200" height="200" viewBox="0 0 30 40" version="1.1">
<circle cx="15" cy="20" r="10" fill="yellow"/>
<circle cx="12" cy="17" r="1.5" fill="black"/>
<circle cx="18" cy="17" r="1.5" fill="black"/>
<path d="m 10,23 c 3.09,2.5 6.56,2.7 10,0" stroke="black" stroke-width="1" stroke-Linecap="round" fill="none">
</path>
</svg>
- Этот файл открываем в векторном редакторе:
- И с помощью рычагов узловых точек меняем линию рта на грусть
Копируем исходный и конечный (грустный) path в другой файл, который будет реализовывать анимацию c помощью изменения атрибута d path
Анимации будет две:
Одна, это переход от улыбки к грусти при первом клике
Вторая от грусти к улыбке, при втором клике
Первая команда анимации выглядит так:
<!-- Анимация линии рта в грустное положение -->
<animate id="evil"
attributeName="d"
begin="indefinite"
dur="0.5s"
to=" m 10,23 c 4.02,-3.57 6.06,-3.4 10,0"
fill="freeze"
restart="whenNotActive" />
Добавляем скрипт JS, который по очереди переключает две анимации
var svg_1 = document.getElementById("svg1"),
evil = document.getElementById("evil"),
happy = document.getElementById("happy");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
evil.beginElement();
flag = false;
} else {
happy.beginElement();
flag = true;
}
});
</script>
<svg id="svg1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" width="200" height="200" viewBox="0 0 30 40" version="1.1">
<circle cx="15" cy="20" r="10" fill="yellow"/>
<circle cx="12" cy="17" r="1.5" fill="black"/>
<circle cx="18" cy="17" r="1.5" fill="black"/>
<path d="m 10,23 c 3.09,2.5 6.56,2.7 10,0" stroke="black" stroke-width="1" stroke-Linecap="round" fill="none">
<!-- Анимация линии рта в грустное положение -->
<animate id="evil"
attributeName="d"
begin="indefinite"
dur="0.5s"
to=" m 10,23 c 4.02,-3.57 6.06,-3.4 10,0"
fill="freeze"
restart="whenNotActive" />
<!-- Анимация линии рта в улыбку -->
<animate id="happy"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m 10,23 c 3.09,2.5 6.56,2.7 10,0"
fill="freeze"
restart="whenNotActive" />
</path>
</svg>
В качестве бонуса, смайлик который подмигивает два раза при клике:
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100" viewBox="0 0 100 100" >
<rect x="10" y="0" rx="15" width="85" height="100" fill="gold" />
<g id="gr1">
<ellipse cx="35" cy="35" ry="10" rx="12" fill="black" >
</ellipse>
<ellipse cx="70" cy="35" ry="10" rx="12" fill="black" >
<animate
attributeName="ry"
dur="1s"
begin="svg1.click"
keyTimes=" 0;0.25;0.5;0.75;1"
values="10;0;5;0;10"
repeatCount="1" />
</ellipse>
</g>
<circle cx="52.5" cy="50" r="30" fill="none" stroke="black" stroke-dashoffset="-6" stroke-dasharray="80 108.49" />
</svg>
Попытаюсь сделать смайлик, как в примере вопроса, но так как не указано, какой должен быть грустный смайлик, то добавлю несколько вариантов. Выбирайте какой больше понравится.
Техника получения анимации из веселого смайлика в грустный образ, та же, что и в первом ответе.
Вариант #1.
Сниппет немного ломает работу скрипта, в отдельно сохраненном файле, плавная анимация работает нормально. Здесь, в сниппете, кликайте по разным участкам смайлика.
var svg_1 = document.getElementById("svg1"),
evil = document.getElementById("evil"),
happy = document.getElementById("happy");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
evil.beginElement();
flag = false;
} else {
happy.beginElement();
flag = true;
}
});
body {
margin: 0;
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: #ffffff;
}
.canvas {
background: #474a51;
width: 200px;
height: 200px;
border: 1px solid #494d4e;
border-radius: 4px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
}
.s0{
fill:none;
stroke-linecap:round;
stroke-linejoin:round;
stroke-width:4px;
stroke:#FF0000;
}
<div class="canvas">
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"preserveAspectRatio="xMidYMid meet" viewBox="0 0 400 400" version="1.1">
<ellipse cx="200" cy="200.8" rx="99.5" ry="100" class="s0" />
<ellipse cx="174.6" cy="189.6" rx="11" ry="11.3" class="s0"/>
<ellipse cx="225" cy="188.8" rx="11" ry="11.3" class="s0"/>
<path d="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5" class="s0">
<!-- Анимация линии рта в грустное положение -->
<animate id="evil"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m128.5 224.4c0 0 8.8-23.6 20.8-7.8 5.6 7.3 10.6 16.3 19.2 22.3 9.6 6.7 16.1-14.6 32.3-14.7 18-0.1 22.5 22.2 32.5 15.4 8.6-5.9 13.2-17.3 19-23.9 13-14.8 21.2 8.8 21.2 8.8"
fill="freeze"
restart="whenNotActive" />
<!-- Анимация линии рта в улыбку -->
<animate id="happy"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5"
fill="freeze"
restart="whenNotActive" />
</path>
</svg>
</div>
Вариант #2.
Немного, изменена форма рта в финальном положении
var svg_1 = document.getElementById("svg1"),
evil = document.getElementById("evil"),
happy = document.getElementById("happy");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
evil.beginElement();
flag = false;
} else {
happy.beginElement();
flag = true;
}
});
body {
margin: 0;
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: #ffffff;
}
.canvas {
background: #474a51;
width: 200px;
height: 200px;
border: 1px solid #494d4e;
border-radius: 4px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
}
.s0{
fill:none;
stroke-linecap:round;
stroke-linejoin:round;
stroke-width:4px;
stroke:#FF0000;
}
<div class="canvas">
<svg id="svg1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 400 400" version="1.1">
<ellipse cx="200" cy="200.8" rx="99.5" ry="100" class="s0" />
<ellipse cx="174.6" cy="189.6" rx="11" ry="11.3" class="s0"/>
<ellipse cx="225" cy="188.8" rx="11" ry="11.3" class="s0"/>
<path d="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5" class="s0">
<!-- Анимация линии рта в грустное положение -->
<animate id="evil"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m128.5 224.4c0 0 8.8-23.6 20.8-7.8 5.6 7.3 10.6 36.1 19.2 42 9.6 6.7 16.1-34.4 32.3-34.5 18-0.1 22.8 40.2 32.8 33.4 8.6-5.9 12.9-35.3 18.7-41.9 13-14.8 21.2 8.8 21.2 8.8"
fill="freeze"
restart="whenNotActive" />
<!-- Анимация линии рта в улыбку -->
<animate id="happy"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5"
fill="freeze"
restart="whenNotActive" />
</path>
</svg>
</div>
Вариант #3.
var svg_1 = document.getElementById("svg1"),
evil = document.getElementById("evil"),
happy = document.getElementById("happy");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
evil.beginElement();
flag = false;
} else {
happy.beginElement();
flag = true;
}
});
body {
margin: 0;
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: #ffffff;
}
.canvas {
background: #474a51;
width: 200px;
height: 200px;
border: 1px solid #494d4e;
border-radius: 4px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
}
.s0{
fill:none;
stroke-linecap:round;
stroke-linejoin:round;
stroke-width:4px;
stroke:#FF0000;
}
<div class="canvas">
<svg id="svg1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 400 400" version="1.1">
<ellipse cx="200" cy="200.8" rx="99.5" ry="100" class="s0" />
<ellipse cx="174.6" cy="189.6" rx="11" ry="11.3" class="s0"/>
<ellipse cx="225" cy="188.8" rx="11" ry="11.3" class="s0"/>
<path d="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5" class="s0">
<!-- Анимация линии рта в грустное положение -->
<animate id="evil"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m127.9 253.8c0 0 9.4-52.9 21.4-37.2 5.6 7.3 15.4 16.9 24 22.9 9.6 6.7 11.9 7.8 28.1 7.7 18-0.1 22.5-4.7 32.5-11.6 8.6-5.9 12.7-13.2 18.4-20 15.1-18.1 19.7 37 19.7 37"
fill="freeze"
restart="whenNotActive" />
<!-- Анимация линии рта в улыбку -->
<animate id="happy"
attributeName="d"
begin="indefinite"
dur="0.5s"
to="m130 200.8c0 0-0.2 22.2 11.9 37.9 5.6 7.3 11.2 14.8 19.8 20.8 9.6 6.7 22.1 11.1 38.3 11 18-0.1 31.5-6.5 41.5-13.4 8.6-5.9 12.9-11.3 17.5-18.8 11.5-18.7 11.6-37.5 11.6-37.5"
fill="freeze"
restart="whenNotActive" />
</path>
</svg>
</div>



