Заливка слоя-маски

128
22 августа 2019, 14:40

Предыстория

Хочу написать некую "систему уровней".
Есть три переменные:

Score - Текущее количество очков;
ScoreNext - Количество очков до нового уровня;
Level - Текущий уровень;

Механику подсчёта опустим, приведу пример:
У пользователя 10 очков, он на 1 уровне, для следующего нужно набрать 20 очков.

1 LVL (0) | #####10%----- | 2 LVL (20) // Не не хватает 10 очков == 10%

Надеюсь понятно, переходим дальше.

Идея

Пользовались программой Steam? Если да, то понять меня будет проще.
Но всё же расскажу:

В Steam используются уровни и их много (на данный момент 4999).

Каждый уровень разбит на "группы"(вертикальные столбцы): Первые 100 - круг, вторые 100 - шестиугольник и т.д. (на скрине видно).
Так же каждая группа разбита ещё на группы (горизонтальные столбцы) - 0-10 - серый цвет изображения, 10-20 - красный и т.д..

Что хочу

Реализовать подобное на SVG.
При этом цвет будет заполнятся как прогресс-бар, снизу-вверх, в процентном соотношение.
Сама "иконка" уровня (вертикальные столбцы на скрине) - маска-слой изображение (SVG или растровое изображение).
При этом всё это будет изменяться через JS\JQ.

Что нужно Как сверстать разметку самого SVG, что "крутить" чтобы JS\JQ кодом можно было управлять "уровнями" (картинкой, цветами, процентным соотношением).

P.s. Вопрос в основном для @Alexandr_TT, он попросил задать его в виде вопроса тут, но если кому-то интересна данная идея - прошу писать ответы или комментарии.

P.s. Слой-маска или Alpha texture.

Ближе к чёрному - прозрачное.

"значок" уровня, пример:

body {background: gray;}
<?xml version="1.0"?> 
<svg xmlns="http://www.w3.org/2000/svg" height="64px" width="64px" viewBox="-30 0 512 512"> 
<path d="m25.601562 162.132812c-4.714843 0-8.535156 3.820313-8.535156 8.535157v8.53125h-8.53125c-4.714844 0-8.535156 3.820312-8.535156 8.535156 0 4.710937 3.820312 8.53125 8.535156 8.53125h8.53125v8.535156c0 4.710938 3.820313 8.53125 8.535156 8.53125 4.710938 0 8.53125-3.820312 8.53125-8.53125v-8.535156h8.535157c4.710937 0 8.53125-3.820313 8.53125-8.53125 0-4.714844-3.820313-8.535156-8.53125-8.535156h-8.535157v-8.53125c0-4.714844-3.820312-8.535157-8.53125-8.535157zm0 0" fill="#FFFFFF"/><path d="m392.535156 17.066406h-8.535156v-8.53125c0-4.714844-3.820312-8.535156-8.535156-8.535156-4.710938 0-8.53125 3.820312-8.53125 8.535156v8.53125h-8.535156c-4.710938 0-8.53125 3.820313-8.53125 8.535156 0 4.710938 3.820312 8.53125 8.53125 8.53125h8.535156v8.535157c0 4.710937 3.820312 8.53125 8.53125 8.53125 4.714844 0 8.535156-3.820313 8.535156-8.53125v-8.535157h8.535156c4.710938 0 8.53125-3.820312 8.53125-8.53125 0-4.714843-3.820312-8.535156-8.53125-8.535156zm0 0" fill="#FFFFFF"/><path d="m443.734375 477.867188h-8.535156v-8.535157c0-4.710937-3.820313-8.53125-8.53125-8.53125-4.714844 0-8.535157 3.820313-8.535157 8.53125v8.535157h-8.53125c-4.714843 0-8.535156 3.820312-8.535156 8.53125 0 4.714843 3.820313 8.535156 8.535156 8.535156h8.53125v8.53125c0 4.714844 3.820313 8.535156 8.535157 8.535156 4.710937 0 8.53125-3.820312 8.53125-8.535156v-8.53125h8.535156c4.710937 0 8.53125-3.820313 8.53125-8.535156 0-4.710938-3.820313-8.53125-8.53125-8.53125zm0 0" fill="#FFFFFF"/><path d="m153.601562 443.734375c-39.085937 0-58.625-24.917969-65.453124-52.652344-7.707032 16.46875-11.589844 34.46875-11.347657 52.652344 0 49.75 91.902344 51.199219 102.398438 51.199219 63.148437 0 102.402343-39.253906 102.402343-102.398438 0-42.242187-28.84375-74.414062-47.875-91.050781 3.671876 29.863281 5.71875 83.539063-20.222656 115.796875-14.632812 17.832031-36.867187 27.648438-59.902344 26.453125zm0 0" fill="#FFFFFF"/><path d="m153.601562 221.867188c0 34.132812 17.066407 85.332031-25.601562 85.332031-17.066406 0-34.132812-17.066407-34.132812-42.664063 0-25.601562 25.597656-51.203125 25.597656-51.203125s-119.464844 25.601563-119.464844 179.203125c0 52.222656 24.917969 89.425782 97.707031 104.019532-21.164062-9.898438-37.972656-26.199219-37.972656-52.820313 0-54.101563 26.964844-81.664063 28.074219-82.859375 2.453125-2.421875 6.121094-3.128906 9.300781-1.792969 3.207031 1.285157 5.304687 4.398438 5.289063 7.851563 0 28.84375 13.484374 59.734375 51.203124 59.734375 17.847657 1.175781 35.175782-6.28125 46.589844-20.054688 31.488282-39.253906 13.570313-122.367187 13.3125-123.136719-.78125-3.375.554688-6.886718 3.378906-8.886718 2.828126-2.003906 6.582032-2.097656 9.507813-.242188 2.984375 1.875 72.277344 45.992188 72.277344 118.1875 0 48.980469-21.421875 86.269532-57.601563 105.214844 69.121094-13.058594 142.933594-52.738281 142.933594-156.417969 0-119.464843-119.464844-145.066406-119.464844-221.867187 0-82.6875 32-109.308594 34.046875-110.847656-4.4375 1.539062-144.980469 53.675781-144.980469 213.25zm0 0" fill="#FFFFFF"/></svg>

UPD

Вот что-то такое, косяков много

$('select').bind('change', function(){ 
	let c = $(this).val(); 
  $('.lvl-icon').attr('class','lvl-icon '+c).find('animate').attr('dur', '5s'); 
}); 
 
$('input').bind('change mousemove', function(){ 
	let c = $(this).val(); 
  $('#change').attr('height', c+'%'); 
});
.lvl-icon { 
  fill: none; 
  stroke: transparent; 
  stroke-width: 10; 
  mask: url(#mask); 
} 
 
.lvl-icon .none {display: none;} 
.lvl-icon.gray {stroke: gray;} 
.lvl-icon.red {stroke: red;} 
.lvl-icon.cur #curcle, 
.lvl-icon.quad #quad {display: block;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<select> 
  <option value="cur gray" selected>lvl 00-10 - gray</option> 
  <option value="cur red">lvl 10-20 - red</option> 
  <option disabled>Ниже будет другая картинка</option> 
  <option value="quad gray">lvl 20-30 - gray</option> 
  <option value="quad red">lvl 30-40 - red</option> 
</select> 
<br> 
<input type="range" min="0" value="50" max="100" step="1"> 
 
<br><br> 
 
<svg class="lvl-icon cur gray" height="200" width="200" viewBox="0 0 200 200"> 
  <defs> 
    <mask id="mask" > 
    <rect fill="white" width="100%" height="100%" />	    
	   <rect id="change" fill="red" x="0" y="0" width="200" height="100" > 
	  </rect>	 
	 </mask>  
  </defs> 
  <circle id="curcle" class="none" cx="100" cy="100" r="90" /> 
  <rect id="quad" class="none" x="0" y="0" width="200" height="200" stroke-width="10" /> 
</svg>

Answer 1

Сначала была лишь анимация заливки в стакан:

#пуск {cursor: pointer;} 
#жижа {stroke:#a50;  
       stroke-linecap: butt;  
       stroke-width:200%;  
       stroke-opacity:.6}
<!-- SVG код --> 
<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<svg version = "1.1" 
     baseProfile="full" 
     xmlns = "http://www.w3.org/2000/svg"  
     xmlns:xlink = "http://www.w3.org/1999/xlink" 
     xmlns:ev = "http://www.w3.org/2001/xml-events" 
     height = "200px"  width = "200px" 
     viewBox="0 0 200 200" preserveAspectRatio="none" 
     style ="border: 1px solid blue;"> 
<title>SVG анимация - линии</title> 
   
  <defs> 
    <path id="менза" fill="url(#жижа)" stroke="#daa" stroke-width="2"  
          d="M 50 200 L 25 10 L 175 10 L 150 200 L 100 200 Z" >  
    </path> 
  </defs> 
  <pattern id="жижа" width="100%" height="100%" > 
    <line id="мера" x1="10" y1="200" x2="10" y2="200">  
      <animate id="лей" attributeName="y2" 
               from="200" to="10" 
               begin="пуск.click" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
      <animate id="пей" attributeName="y2" 
               from="10" to="200" 
               begin="лей.end+.3s" dur="2s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
    </line> 
  </pattern> 
   
  <use  xlink:href="#менза" /> 
   
  <g id="пуск">  
    <rect x="5" y="160" rx="3" ry="3"  
          width="90" height="30"fill="#552" stroke="#2af" /> 
    <text x="15" y="180" fill="white"  
          font-size="16" font-weight="bold" font-family="Arial" > 
          Наливай 
    </text> 
 </g>  
</svg>

Однако это была лишь разминка.
А не пора ли нам показать настоящий "тринкен-процесс", (?)

На троих:

#лить, #пить {cursor: pointer;} 
#жижа {stroke:#f4a; stroke-width:200%;  
       stroke-linecap: butt; stroke-opacity:.8; 
} 
#тринк {box-shadow: #fff 0 0 8px 4px inset; background:  
  repeating-linear-gradient(45deg, transparent, #999, transparent .8%) fixed; 
}
<!-- SVG код --> 
<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<svg id="тринк" version = "1.1" baseProfile="full" 
     width = "200px" height = "200px"  
     viewBox="0 0 200 200" preserveAspectRatio="none">  
  <defs> 
    <path id="штофф" fill="url(#жижа)" stroke="#0aa" stroke-width="2"  
          d="M 50,190 L 25,10 L 175,10 L 150,190 L 50,190 Z" />  
    <linearGradient id="блик" x1="0" x2="100%"> 
         <stop offset="0%"  stop-color="#0aa"/> 
         <stop offset="38%" stop-color="#eed"/> 
         <stop offset="80%" stop-color="#0aa"/> 
    </linearGradient> 
  </defs> 
 
  <pattern id="жижа" width="100%" height="100%"> 
    <rect width="100%" height="100%"  fill="url(#блик)" fill-opacity=".8" 
          stroke="#0aa"  stroke-width="2"/> 
    <line id="мера" x1="10" y1="200" x2="10" y2="200"/>  
  </pattern> 
   
  <use  xlink:href="#штофф" /> 
   
    <g id="лить">  
    <rect x="55" y="150" rx="3" ry="3"  
          width="90" height="30" fill="#da0" stroke="#288" stroke-width="2"/> 
    <text font-size="16" font-weight="bold" font-family="Arial"> 
      <tspan id="" x="75" y="170" fill="#000">-Лей!-</tspan><!--  --> 
    </text> 
    </g>  
    <g id="пить" style="opacity:0;">  
    <rect x="55" y="30" rx="3" ry="3"  
          width="90" height="30" fill="#0dd" stroke="#288" stroke-width="2"/> 
    <text font-size="16" font-weight="bold" font-family="Arial"><!--  --> 
      <tspan id="" x="75" y="50" fill="#000">-Пей!-</tspan> 
    </text> 
    </g>  
      <animate id="лей" xlink:href="#мера" attributeName="y2" 
               from="200" to="10" 
               begin="лить.click" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
      <animate id="лью" xlink:href="#лить" attributeName="opacity" 
               from="1" to="0" 
               begin="лить.click" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
      <animate id="лил" xlink:href="#пить" attributeName="opacity" 
               from="0" to="1" 
               begin="лей.end" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
   
      <animate id="пей" xlink:href="#мера" attributeName="y2" 
               from="10" to="200" 
               begin="пить.click" dur="2s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
      <animate id="пью" xlink:href="#пить" attributeName="opacity" 
               from="1" to="0" 
               begin="пить.click" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
      <animate id="пил" xlink:href="#лить" attributeName="opacity" 
               from="0" to="1" 
               begin="пей.end" dur="1s" 
               repeatCount="1"  
               fill="freeze" 
               restart="whenNotActive" /> 
</svg> 
<svg width="200px" height="200px" ><use  xlink:href="#штофф"/></svg> 
<svg width="200px" height="200px" ><use  xlink:href="#штофф"/></svg>

Остальное - по мере поступления дополнительных комментариев...

Answer 2

Не совсем уверен, не ушел ли я не в ту степь) Не svg, но довольно похоже. Работает с -webkit- в Chrome (в других не проверял). Изображение выступает в роли маски, применяется через -webkit-mask-image. Цвет меняется на каждом уровне в зависимости от общего прогресса.

document.getElementById("range").addEventListener("input", e => { 
  let val = e.target.value; 
  let max = 1000; 
  let lvlMax = 200; 
 
  let currentlvlProgress = val % lvlMax / lvlMax * 100; 
  let currentLvl = Math.ceil(val / lvlMax); 
  let globalProgress = val / max * 100; 
  let hue = currentLvl / (max / lvlMax) * 360; 
 
  document.getElementById("info").innerHTML = ` 
    1 уровень: <b>${lvlMax}</b> очков<br> 
    Всего уровней: <b>${max / lvlMax}</b><br> 
    Текущий уровень: <b>${currentLvl}</b><br> 
    Прогресс в уровне: <b>${currentlvlProgress}</b><br> 
    Глобальный прогресс: <b>${globalProgress}</b><br> 
    Изменение цвета: <b>${hue}</b> градусов<br> 
  `; 
 
  document.getElementById("progress").style.height = `${currentlvlProgress}%`; 
  document.getElementById("progress").style.filter = `hue-rotate(${hue}deg)`; 
});
body { 
  display: flex; 
  flex-direction: column; 
  font-size: 14px; 
} 
 
body>* { 
  margin-bottom: 5px; 
} 
 
#icon { 
  position: relative; 
  width: 40px; 
  height: 40px; 
  mask-size: 100% 100%; 
  mask-position: 0 0; 
  mask-image: url("https://cdn.vectorlogosupply.com/logos/large/2x/instagram-glyph-1-png-transparent-logo.png"); 
  -webkit-mask-size: 100% 100%; 
  -webkit-mask-position: 0 0; 
  -webkit-mask-image: url("https://cdn.vectorlogosupply.com/logos/large/2x/instagram-glyph-1-png-transparent-logo.png"); 
  -moz-mask-size: 100% 100%; 
  -moz-mask-position: 0 0; 
  -moz-mask-image: url("https://cdn.vectorlogosupply.com/logos/large/2x/instagram-glyph-1-png-transparent-logo.png"); 
} 
 
#icon>#progress { 
  position: absolute; 
  bottom: 0; 
  left: 0; 
  height: 0%; 
  width: 100%; 
  background-color: red; 
  z-index: 2; 
}
<div id="icon"> 
  <div id="progress"></div> 
</div> 
 
<span id="info"></span> 
 
<input type="range" max="1000" value="0" id="range">

READ ALSO
Как запустить скрипт ещё раз?

Как запустить скрипт ещё раз?

У меня есть вот такой скрипт

103
Почему тернарный оператор выдает undefined в цикле?

Почему тернарный оператор выдает undefined в цикле?

Не могу понять, почему выходит такой ответ в этом цикле, скажите пожалуйста

115
Вывод надписи на столбике Chart js

Вывод надписи на столбике Chart js

Все привет, в первые сталкиваюсь с Chart Js и сразу же встал в тупикТребуется вывести данные из базы данных mysql сделать подсчеты в php и вывести...

128