我正在尝试制作一种效果,当单击技能图标时,它会进入“冷却”状态,类似于《魔兽世界》等典型 MMO 中技能的冷却状态。
我找到了下面的代码来实现这样的效果,但它没有文档记录,我不太理解它,但想对其进行更改。
html:
<table>
<tr>
<td>
<div class="skill"></div>
</td>
<td>
<div class="skill"></div>
</td>
<td>
<div class="skill"></div>
</td>
<td>
<div class="skill"></div>
</td>
<td>
<div class="skill"></div>
</td>
</tr>
</table>
CSS:
.skill {
margin: 0px;
padding: 0px;
position: relative;
border: 1px solid #36393E;
border-radius: 5%;
width: 44px;
height: 44px;
overflow: hidden;
background-color: transparent;
background-image: url("skill.png");
background-repeat: no-repeat;
background-size: 100%;
}
.cooldown {
position: absolute;
opacity: 0.8;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
js:
function cooldown(container, percent) {
var div = $(container);
div.empty();
var total = 100;
if(percent < total) {
var data = [percent, total - percent];
var width = div.width();
var height = div.height();
var cx = width / 2;
var cy = height / 2;
var r = cx * Math.SQRT2;
var colors = [null, '#AAA'];
var svgns = "http://www.w3.org/2000/svg";
var chart = document.createElementNS(svgns, "svg:svg");
chart.setAttribute("width", width);
chart.setAttribute("height", height);
chart.setAttribute("viewBox", "0 0 " + width + " " + height);
var angles = []
for(var i = 0; i < data.length; i++) angles[i] = data[i] / total * Math.PI * 2;
startangle = 0;
for(var i = 0; i < data.length; i++) {
var endangle = startangle + angles[i];
var x1 = cx + r * Math.sin(startangle);
var y1 = cy - r * Math.cos(startangle);
var x2 = cx + r * Math.sin(endangle);
var y2 = cy - r * Math.cos(endangle);
var big = 0;
if (endangle - startangle > Math.PI) big = 1;
var path = document.createElementNS(svgns, "path");
var d = "M " + cx + "," + cy + " L " + x1 + "," + y1 +
" A " + r + "," + r + " 0 " + big + " 1 " +
x2 + "," + y2 + " Z";
path.setAttribute("d", d);
if(colors[i]) {
path.setAttribute("fill", colors[i]);
} else {
path.setAttribute("opacity", 0);
}
chart.appendChild(path);
startangle = endangle;
}
chart.setAttribute('overflow', 'hidden');
div.append(chart);
}
}
$(window).ready(function() {
$('.skill').each(function() {
var skill = $(this);
var div = $('<div />').appendTo(skill).addClass('cooldown');
var radius = div.parent().width() / 2;
skill.click(function() {
$({pct: 0}).animate({pct: 100}, {
duration: 5000,
step: function (curLeft) { cooldown(div, curLeft); }
});
});
});
});
我设法对其进行了一项我想要的更改,即添加带有冷却时间的文本,如下所示:(尚未在 js 中完成更新,但我认为这是我自己可以做的事情)
<div class="skill"><div class="text">5</div></div>
.skill .text {
font-size: 14px;
color: #E1E5EB;
text-shadow: 1px 1px 1px #000000;
line-height: 44px;
text-align: center;
background-color: transparent;
}
我需要帮助的其他更改是:
逆时针填充而不是顺时针填充。
翻转填充/未填充部分的不透明度(当前为 填充的部分是深色的,未填充的部分是浅色的,但我想要相反的)
另一种方法是将conic-gradient与一些CSS变量一起使用。这样我们就不需要构建 SVG。
PS:JS中有注释解释它是如何工作的。
const SECOND_IN_MS = 1000;
const UPDATE_INTERVAL = SECOND_IN_MS / 60; // Update 60 times per second (60 FPS)
const SKILL_CLASS = 'skill';
const DISABLED_CLASS = 'disabled';
// Cooldowns per skill in milliseconds
const COOLDOWN_MAP = new Map([
['run', 1000],
['jump', 2000],
['crawl', 3000],
['slide', 4000],
['tumble', 5000],
]);
// Get skills table from the DOM
const skillsTable = document.querySelector('.skills-table');
// Activate clicked skill
const activateSkill = (event) => {
const {target} = event;
// Exit if we click on anything that isn't a skill
if(!target.classList.contains(SKILL_CLASS)) return;
target.classList.add(DISABLED_CLASS);
target.style = '--time-left: 100%';
// Get cooldown time
const skill = target.dataset.skill;
let time = COOLDOWN_MAP.get(skill) - UPDATE_INTERVAL;
// Update remaining cooldown
const intervalID = setInterval(() => {
// Pass remaining time in percentage to CSS
const passedTime = time / COOLDOWN_MAP.get(skill) * 100;
target.style = `--time-left: ${passedTime}%`;
// Display time left
target.textContent = (time / SECOND_IN_MS).toFixed(2);
time -= UPDATE_INTERVAL;
// Stop timer when there is no time left
if(time < 0) {
target.textContent = '';
target.style = '';
target.classList.remove(DISABLED_CLASS);
clearInterval(intervalID);
}
}, UPDATE_INTERVAL);
}
// Add click handler to the table
skillsTable.addEventListener('click', activateSkill, false);
.skill {
position: relative;
border: 1px solid #36393E;
border-radius: 5%;
width: 44px;
height: 44px;
overflow: hidden;
cursor: pointer;
}
/* Prevents you from clicked the button multiple times */
.skill.disabled {
pointer-events: none;
}
/* Makes sure we click the skill not anything in it */
.skill > * {
pointer-events: none;
}
.skill::before {
content: "";
background: conic-gradient(
rgba(0, 0, 0, 0.7) var(--time-left),
rgba(0, 0, 0, 0.1) var(--time-left));
position: absolute;
opacity: 0.8;
top: 0;
left: 0p;
height: 100%;
width: 100%;
}
<table class='skills-table'>
<tr>
<td>
<!-- data-skill refers to the COOLDOWN_MAP in JS -->
<div class="skill" data-skill='run'></div>
</td>
<td>
<!-- data-skill refers to the COOLDOWN_MAP in JS -->
<div class="skill" data-skill='jump'></div>
</td>
<td>
<!-- data-skill refers to the COOLDOWN_MAP in JS -->
<div class="skill" data-skill='crawl'></div>
</td>
<td>
<!-- data-skill refers to the COOLDOWN_MAP in JS -->
<div class="skill" data-skill='slide'></div>
</td>
<td>
<!-- data-skill refers to the COOLDOWN_MAP in JS -->
<div class="skill" data-skill='tumble'></div>
</td>
</tr>
</table>
如果你们允许我分享我的代码。它基于 @Reyno 的代码,但我使用 @keyframes 而不是 vanilla js。在我看来,它更好,代码也更简单。
请按照以下步骤:
const SECOND_IN_MS = 1000;
const UPDATE_INTERVAL = SECOND_IN_MS / 30; // frames per second
const startTimer = (target) => {
// cooldown time
const datasetTime = target.dataset.time;
let time = parseInt(datasetTime);
target.style = `--time: ${(datasetTime / SECOND_IN_MS)}s;`;
// update remaining cooldown
const intervalID = setInterval(() => {
// display time left
target.textContent = (time / SECOND_IN_MS).toFixed(2);
time -= UPDATE_INTERVAL;
// stop timer when there is no time left
if (time < 0) {
target.textContent = '';
target.style = '';
clearInterval(intervalID);
target.remove();
}
}, UPDATE_INTERVAL);
}
document.querySelector('#timer-creator').addEventListener('click', (event) => {
const timerSecondsElement = document.querySelector("#timer-seconds");
if (!timerSecondsElement.value.trim()) return;
const divElement = document.createElement('div');
divElement.classList.add('skill');
divElement.dataset.time = timerSecondsElement.value * SECOND_IN_MS;
timerSecondsElement.value = '';
document.body.appendChild(divElement);
startTimer(divElement);
})
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.skill {
position: relative;
border: 1px solid #ddd;
border-radius: 50%;
width: 100px;
height: 100px;
overflow: hidden;
cursor: pointer;
display: flex;
justify-items: center;
align-items: center;
justify-content: center;
color: #555;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.skill::before {
content: "";
background: conic-gradient(rgba(0, 0, 0, 0.3) var(--angle), rgba(0, 0, 0, 0.1) var(--angle));
animation: cooldown-animation var(--time) linear infinite;
position: absolute;
opacity: 0.9;
top: 0;
left: 0p;
height: 100%;
width: 100%;
}
@keyframes cooldown-animation {
0% {
--angle: 0deg;
}
100% {
--angle: 360deg;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cooldown animation</title>
</head>
<body>
<div>
<input id="timer-seconds" type="text" placeholder="time in seconds..."/>
<button id="timer-creator">Create timer</button>
</div>
</body>
</html>
希望你喜欢。
干杯!