Хочу сделать анимацию спидометра с помощью canvas. Но мне необходимо, чтобы стрелка спидометра была треугольной формы и при изменении значения, она показывала на необходимое значение, но ее основание всегда оставалось в центре. Подскажите какую формулу или алгоритм необходимо применить для этого. Код представлен чуть ниже.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
const canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
// general settings
middleX = canvas.width / 2,
middleY = canvas.height / 2,
radius = 240,
counterClockwise = false,
// ticks settings
tickWidth = canvas.width / 100,
// tickColor = "#746845";
tickOffsetFromArc = canvas.width / 40,
// Center circle settings
centerCircleRadius = canvas.width / 20,
centerCircleColor = "#ccc",
centerCircleBorderWidth = canvas.width / 100,
// Arrow settings
arrowValueIndex = .73,
arrowColor = "#464646",
arrowWidth = canvas.width / 50,
// numbers
digits = [0, 20, 40, 50, 60, 70, 80, 90, 100],
digitsColor = "#0a0a0a",
digitsFont = "bold 20px Tahoma",
digitsOffsetFromArc = canvas.width / 15,
//zones
zonesCount = digits.length - 1;
// beginning and ending of our arc. Sets by radius*pi
let startAngleIndex = .75,
endAngleIndex = 2.25,
step = (endAngleIndex - startAngleIndex) / zonesCount;
/*draw zones*/
let DrawZones = function () {
const greyZonesCount = zonesCount / 1.6;
greenZonesCount = zonesCount - greyZonesCount,
startAngle = (startAngleIndex - 0.02) * Math.PI,
endGreyAngle = (startAngleIndex + greyZonesCount * step) * Math.PI,
endGreenAngle = (endAngleIndex + 0.02) * Math.PI,
//zones' options
sectionOptions = [
{
startAngle: startAngle,
endAngle: endGreyAngle,
color: "#e7e7e7",
zoneLineWidth: 2
},
{
startAngle: endGreyAngle,
endAngle: endGreenAngle,
color: "#13b74b",
zoneLineWidth: 5
},
];
this.DrawZone = function (options) {
ctx.beginPath();
ctx.arc(middleX, middleY, radius, options.startAngle, options.endAngle, counterClockwise);
ctx.lineWidth = options.zoneLineWidth;
ctx.strokeStyle = options.color;
ctx.lineCap = "round";
ctx.stroke();
};
sectionOptions.forEach(options => this.DrawZone(options));
};
/*draw dots*/
let DrawTicks = function () {
startAngleIndex = .73,
endAngleIndex = 2.27,
step = (endAngleIndex - startAngleIndex) / zonesCount;
this.DrawTick = function (angle,count) {
let fromX = middleX + (radius - tickOffsetFromArc) * Math.cos(angle),
fromY = middleY + (radius - tickOffsetFromArc) * Math.sin(angle),
toX = middleX + (radius + tickOffsetFromArc) * Math.cos(angle),
toY = middleY + (radius + tickOffsetFromArc) * Math.sin(angle),
centerOfDotX=(fromX+toX)/2,
centerOfDotY=(fromY+toY)/2;
ctx.beginPath();
ctx.arc(centerOfDotX,centerOfDotY,6,0,Math.PI*2,true);
if (count<6){
switch (count) {
case 1:
case 2:
case 3:
ctx.fillStyle="#FF0000";
break;
default:
ctx.fillStyle="#F9AF00";
break;
}
}else{
ctx.fillStyle="#FFF";
ctx.strokeStyle="#13B74B";
ctx.shadowColor = "#a8bbaa";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.stroke();
}
ctx.fill();
ctx.closePath();
ctx.shadowBlur =0;
};
let count=0;
for (let i = startAngleIndex; i <= endAngleIndex; i += step) {
let angle = i * Math.PI;
count++;
this.DrawTick(angle,count);
}
};
//draw numbers
let DrawDigits = function () {
let angleIndex = startAngleIndex;
digits.forEach(function (digit) {
let angle = angleIndex * Math.PI,
x = middleX + (radius - digitsOffsetFromArc) * Math.cos(angle),
y = middleY + (radius - digitsOffsetFromArc) * Math.sin(angle);
angleIndex += step;
ctx.font = digitsFont;
ctx.fillStyle = digitsColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(digit, x, y);
});
};
/*draw arrow РИСОВАНИЕ СТРЕЛКИ*/
let DrawArrow = function () {
let arrowAngle = arrowValueIndex * Math.PI;
let toX = middleX + (radius) * Math.cos(arrowAngle)+50;
let toY = middleY + (radius) * Math.sin(arrowAngle)-50;
ctx.beginPath();
ctx.moveTo(middleX, middleY);
ctx.lineTo(toX, toY);
ctx.strokeStyle = arrowColor;
ctx.lineWidth = arrowWidth;
ctx.stroke();
ctx.closePath();
};
window.onload=()=>{
DrawZones();
DrawTicks();
DrawDigits();
DrawArrow();
};
</script>
</body>
</html>
Вот еще вариант, старый уничтожать не стал, пусть будет второй ответ:
let canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
// general settings
middleX = canvas.width / 2,
middleY = canvas.height / 2,
radius = 240,
counterClockwise = false,
// ticks settings
tickWidth = canvas.width / 100,
// tickColor = "#746845";
tickOffsetFromArc = canvas.width / 40,
// Center circle settings
centerCircleRadius = canvas.width / 20,
centerCircleColor = "#ccc",
centerCircleBorderWidth = canvas.width / 100,
// Arrow settings
arrowValueIndex = 0,
arrowColor = "#464646",
arrowWidth = canvas.width / 150,
// numbers
digits = [0, 20, 40, 50, 60, 70, 80, 90, 100],
digitsColor = "#0a0a0a",
digitsFont = "bold 20px Tahoma",
digitsOffsetFromArc = canvas.width / 15,
//zones
zonesCount = digits.length - 1;
// beginning and ending of our arc. Sets by radius*pi
let startAngleIndex = .75,
endAngleIndex = 2.25,
step = (endAngleIndex - startAngleIndex) / zonesCount;
/*draw zones*/
let DrawZones = function () {
const greyZonesCount = zonesCount / 1.6;
greenZonesCount = zonesCount - greyZonesCount,
startAngle = (startAngleIndex - 0.02) * Math.PI,
endGreyAngle = (startAngleIndex + greyZonesCount * step) * Math.PI,
endGreenAngle = (endAngleIndex + 0.02) * Math.PI,
//zones' options
sectionOptions = [{
startAngle: startAngle,
endAngle: endGreyAngle,
color: "#e7e7e7",
zoneLineWidth: 2
},{
startAngle: endGreyAngle,
endAngle: endGreenAngle,
color: "#13b74b",
zoneLineWidth: 5
}];
this.DrawZone = function (options) {
ctx.beginPath();
ctx.arc(middleX, middleY, radius, options.startAngle, options.endAngle, counterClockwise);
ctx.lineWidth = options.zoneLineWidth;
ctx.strokeStyle = options.color;
ctx.lineCap = "round";
ctx.stroke();
};
sectionOptions.forEach(options => this.DrawZone(options));
};
/*draw dots*/
let DrawTicks = function () {
startAngleIndex = .73,
endAngleIndex = 2.27,
step = (endAngleIndex - startAngleIndex) / zonesCount;
this.DrawTick = function (angle,count) {
let fromX = middleX + (radius - tickOffsetFromArc) * Math.cos(angle),
fromY = middleY + (radius - tickOffsetFromArc) * Math.sin(angle),
toX = middleX + (radius + tickOffsetFromArc) * Math.cos(angle),
toY = middleY + (radius + tickOffsetFromArc) * Math.sin(angle),
centerOfDotX=(fromX+toX)/2,
centerOfDotY=(fromY+toY)/2;
ctx.beginPath();
ctx.arc(centerOfDotX,centerOfDotY,6,0,Math.PI*2,true);
if (count<6){
switch (count) {
case 1:
case 2:
case 3:
ctx.fillStyle="#FF0000";
break;
default:
ctx.fillStyle="#F9AF00";
break;
}
} else {
ctx.fillStyle="#FFF";
ctx.strokeStyle="#13B74B";
ctx.shadowColor = "#a8bbaa";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.stroke();
}
ctx.fill();
ctx.closePath();
ctx.shadowBlur =0;
};
let count=0;
for (let i = startAngleIndex; i <= endAngleIndex; i += step) {
let angle = i * Math.PI;
count++;
this.DrawTick(angle,count);
}
};
//draw numbers
let DrawDigits = function () {
let angleIndex = startAngleIndex;
digits.forEach(function (digit) {
let angle = angleIndex * Math.PI,
x = middleX + (radius - digitsOffsetFromArc) * Math.cos(angle),
y = middleY + (radius - digitsOffsetFromArc) * Math.sin(angle);
angleIndex += step;
ctx.font = digitsFont;
ctx.fillStyle = digitsColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(digit, x, y);
});
};
/*draw arrow РИСОВАНИЕ СТРЕЛКИ*/
let DrawArrow = function () {
ctx.beginPath();
ctx.moveTo(middleX-17, middleY-47);
ctx.lineTo(middleX, middleY-180);
ctx.lineTo(middleX+17, middleY-47);
ctx.strokeStyle = arrowColor;
ctx.lineWidth = arrowWidth;
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(middleX, middleY, 50, Math.PI/8- Math.PI/2, 2 * Math.PI-Math.PI/8- Math.PI/2);
ctx.stroke();
};
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
DrawZones();
DrawTicks();
DrawDigits();
ctx.translate(middleX,middleY);
ctx.rotate(arrowValueIndex);
ctx.translate(-middleX,-middleY);
DrawArrow();
ctx.translate(middleX,middleY);
ctx.rotate(-arrowValueIndex);
ctx.translate(-middleX,-middleY);
}
window.onload = draw
function val(value) {
let sector = Math.PI*0.385
if (value < 40)
arrowValueIndex = value/40*sector - sector*2;
else
arrowValueIndex = (value-40)/60*sector*3 - sector;
document.querySelector('span').textContent = value;
draw();
}
<input type="range" value="60" onmousemove="val(this.value)"><span></span><br>
<canvas id="canvas" width="500" height="500"></canvas>
Печально что распределение шкалы неравномерное, если бы оно было равномерное можно было бы сделать нечто такое
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height) //очистка канвы
ctx.translate(middleX,middleY); // сдвиг в центр
ctx.rotate(arrowValueIndex); // поворот табло
ctx.translate(-middleX,-middleY); // сдвиг обратно
DrawZones();
DrawTicks(); // рисует табло
DrawDigits();
ctx.translate(middleX,middleY); // сдвиг в центр
ctx.rotate(-arrowValueIndex); // обратный поворот табло
ctx.translate(-middleX,-middleY); // сдвиг обратно
DrawArrow(); // стрелка
}
let canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
// general settings
middleX = canvas.width / 2,
middleY = canvas.height / 2,
radius = 240,
counterClockwise = false,
// ticks settings
tickWidth = canvas.width / 100,
// tickColor = "#746845";
tickOffsetFromArc = canvas.width / 40,
// Center circle settings
centerCircleRadius = canvas.width / 20,
centerCircleColor = "#ccc",
centerCircleBorderWidth = canvas.width / 100,
// Arrow settings
arrowValueIndex = 0,
arrowColor = "#464646",
arrowWidth = canvas.width / 150,
// numbers
digits = [0, 20, 40, 50, 60, 70, 80, 90, 100],
digitsColor = "#0a0a0a",
digitsFont = "bold 20px Tahoma",
digitsOffsetFromArc = canvas.width / 15,
//zones
zonesCount = digits.length - 1;
// beginning and ending of our arc. Sets by radius*pi
let startAngleIndex = .75,
endAngleIndex = 2.25,
step = (endAngleIndex - startAngleIndex) / zonesCount;
/*draw zones*/
let DrawZones = function () {
const greyZonesCount = zonesCount / 1.6;
greenZonesCount = zonesCount - greyZonesCount,
startAngle = (startAngleIndex - 0.02) * Math.PI,
endGreyAngle = (startAngleIndex + greyZonesCount * step) * Math.PI,
endGreenAngle = (endAngleIndex + 0.02) * Math.PI,
//zones' options
sectionOptions = [
{
startAngle: startAngle,
endAngle: endGreyAngle,
color: "#e7e7e7",
zoneLineWidth: 2
},
{
startAngle: endGreyAngle,
endAngle: endGreenAngle,
color: "#13b74b",
zoneLineWidth: 5
},
];
this.DrawZone = function (options) {
ctx.beginPath();
ctx.arc(middleX, middleY, radius, options.startAngle, options.endAngle, counterClockwise);
ctx.lineWidth = options.zoneLineWidth;
ctx.strokeStyle = options.color;
ctx.lineCap = "round";
ctx.stroke();
};
sectionOptions.forEach(options => this.DrawZone(options));
};
/*draw dots*/
let DrawTicks = function () {
startAngleIndex = .73,
endAngleIndex = 2.27,
step = (endAngleIndex - startAngleIndex) / zonesCount;
this.DrawTick = function (angle,count) {
let fromX = middleX + (radius - tickOffsetFromArc) * Math.cos(angle),
fromY = middleY + (radius - tickOffsetFromArc) * Math.sin(angle),
toX = middleX + (radius + tickOffsetFromArc) * Math.cos(angle),
toY = middleY + (radius + tickOffsetFromArc) * Math.sin(angle),
centerOfDotX=(fromX+toX)/2,
centerOfDotY=(fromY+toY)/2;
ctx.beginPath();
ctx.arc(centerOfDotX,centerOfDotY,6,0,Math.PI*2,true);
if (count<6){
switch (count) {
case 1:
case 2:
case 3:
ctx.fillStyle="#FF0000";
break;
default:
ctx.fillStyle="#F9AF00";
break;
}
}else{
ctx.fillStyle="#FFF";
ctx.strokeStyle="#13B74B";
ctx.shadowColor = "#a8bbaa";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.stroke();
}
ctx.fill();
ctx.closePath();
ctx.shadowBlur =0;
};
let count=0;
for (let i = startAngleIndex; i <= endAngleIndex; i += step) {
let angle = i * Math.PI;
count++;
this.DrawTick(angle,count);
}
};
//draw numbers
let DrawDigits = function () {
let angleIndex = startAngleIndex;
digits.forEach(function (digit) {
let angle = angleIndex * Math.PI,
x = middleX + (radius - digitsOffsetFromArc) * Math.cos(angle),
y = middleY + (radius - digitsOffsetFromArc) * Math.sin(angle);
angleIndex += step;
ctx.font = digitsFont;
ctx.fillStyle = digitsColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(digit, x, y);
});
};
/*draw arrow РИСОВАНИЕ СТРЕЛКИ*/
let DrawArrow = function () {
ctx.beginPath();
ctx.moveTo(middleX-17, middleY-47);
ctx.lineTo(middleX, middleY-180);
ctx.lineTo(middleX+17, middleY-47);
ctx.strokeStyle = arrowColor;
ctx.lineWidth = arrowWidth;
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(middleX, middleY, 50, Math.PI/8- Math.PI/2, 2 * Math.PI-Math.PI/8- Math.PI/2);
ctx.stroke();
};
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.translate(middleX,middleY);
ctx.rotate(arrowValueIndex);
ctx.translate(-middleX,-middleY);
DrawZones();
DrawTicks();
DrawDigits();
ctx.translate(middleX,middleY);
ctx.rotate(-arrowValueIndex);
ctx.translate(-middleX,-middleY);
DrawArrow();
}
window.onload = draw;
function val(value) {
let sector = Math.PI*0.385
if (value < 40)
arrowValueIndex = value/40*sector - sector*2;
else
arrowValueIndex = (value-40)/60*sector*3 - sector;
document.querySelector('span').textContent = value;
draw()
}
<input type="range" value="60" onmousemove="val(this.value)"><span></span><br>
<canvas id="canvas" width="500" height="500"></canvas>
С canvas не работал, но посмотрите данные реализации, возможно помогут: http://www.knowstack.com/html5-canvas-speedometer/
https://github.com/vjt/canvas-speedometer
помогите пожалуйста с меню, нужно что бы на всех экранах оно было равно ширине 70% то есть на всю область сайтаКак можно это реализовать ?? давать...
Есть объект audio-postКак получить все идентификаторы <li> этого объекта? Как на JS отфильтровать полученные идентификаторы до чисел?
Есть массив обьектов fruits, из которого, обьекты могут добавляются в массив selected