Как сделать смайлик, у которого улыбка сменяется на грусть при клике

помогите с определением координат 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 шт):

Автор решения: Alexandr_TT

нужно анимировать рот чтобы он из улыбки превращался в грусть

Нужно создать два смайлика:

  1. Улыбка, радость - уголки губ вверх
  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>     

→ Ссылка
Автор решения: Alexandr_TT

Попытаюсь сделать смайлик, как в примере вопроса, но так как не указано, какой должен быть грустный смайлик, то добавлю несколько вариантов. Выбирайте какой больше понравится.

Техника получения анимации из веселого смайлика в грустный образ, та же, что и в первом ответе.

Вариант #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>
 

→ Ссылка