Необходимость настройки
рисоватьНижний слой мощный.Языки, которые мы используем на всех концах, предназначены только для поиска точек соприкосновения, сохраняя при этом различия в темпах современного поиска пользовательского интерфейса и взаимодействии пользовательских предпочтений, извлекая и инкапсулируя элементы управления пользовательского интерфейса с отдельными стилями, конечно, сталкиваются с триллионами клиентов. Пользовательский интерфейс различных платформ. Библиотека не может удовлетворить все потребности клиентов. Конечно, настраиваемость языка также означает его мощь. Почти каждая платформа предоставляет интерфейс для разработчиков, чтобы они могли создавать возможности своего пользовательского интерфейса, что скорее всего удовлетворит потребности клиентов.ECharts
Как мощный внешний график K-line и другие инструменты рисования, можно сказать, что он имеет то, что должен, и чрезвычайно кокетлив. Но потребности пользователей и продуктов никогда не могут быть удовлетворены библиотекой. Конечно, индивидуальный рисунок в качестве техника также должен быть техникой, которую необходимо освоить. Как внешний мобильный терминал нашего продукта, мы должны сделать его уникальным и неповторимым. Так обычай от нашего技术岗位
,技术本身
,亿万用户不同需求
...отправление, "обычайЭто необходимо".
2. Электронные карты
EChartsТе, кто использовал его, знают, что он чрезвычайно богат и имеет прибамбасы. Ничего не написано про использование библиотеки? Наша цель сегодня - научиться анализировать и писать эффект от ECharts самостоятельно, а не использовать библиотеку Echarts.Хотя я никогда не писал фронтенд, но если у вас есть API, вы можете шаг за шагом спускаться вниз.
следующее:
линейный график
К-линейный график
К-линейный график
....конечно, есть еще много.
3. Понимание холста
В отличие от Android и Flutter и т. д. Canvas — это не настоящий холст в HTML5.<canvas>Сам элемент не имеет возможности рисования (это просто контейнер для графики) — вам нужно использовать скрипт, чтобы сделать фактическое рисование.getContext() 方法可返回一个对象
, который предоставляет методы и свойства для рисования на холсте. В HTML5 объект getContext("2d") можно получить с помощью тега Canvas, который предоставляет множество свойств и методов рисования, которые можно использовать для рисования текста, линий, прямоугольников, кругов и т. д. на холсте.
1. Создание холста
Сначала мы получаем метку контейнера Canvas через getElementById, а затем получаем объект рисования через canvas.getContext("2d").
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id="canvas"/>
<script>
//getElementById() 来访问 canvas 元素
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.fillStyle= 'rgb(222,155,155)';
context.fillRect(0,0,canvas.width,canvas.height);
</script>
</body>
</html>
2. Установите ширину, высоту и цвет фона холста.
Задайте размер холста с помощью свойств ширины и высоты холста. Задайте цвет области рисования через свойство fillStyle. fillRect, чтобы установить размер области рисования в форме расстояния с фиксированной шириной и высотой в верхнем левом углу точки координат.
canvas.width устанавливает ширину холста
canvas.height устанавливает высоту холста
context.fillStyle устанавливает цвет заливки
Предоставляется из контекста формы. FillRect
Атрибуты | эффект |
---|---|
fillStyle | Установите стиль заливки, цвет, градиент и т. д. |
fillRect() | Определите положение и размер заполненного прямоугольника |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id="canvas"/>
<script>
//getElementById() 来访问 canvas 元素
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width=400;
canvas.height=200;
//获取绘制的对象
const context = canvas.getContext("2d");
//设置填充的颜色是颜色渐变等
context.fillStyle= 'rgb(222,155,155)';
//填充区域为矩形,来定义位置和大小
context.fillRect(0,0,canvas.width,canvas.height);
</script>
</body>
</html>
Установите ширину и высоту холста на width=1000;height=500;
Эффект следующий:
//画布宽高
canvas.width=1000;
canvas.height=5000;
...
context.fillStyle= 'rgb(222,155,155)';
3. Установите цвет градиента
Градиенты всегда были чрезвычайно распространенным эффектом в пользовательском интерфейсе. Это не только добавляет красоты, но и является незаменимым секретом в дороге. Далее, давайте испытаем градиент передней части.
Линейный градиент
createLinearGradient(x0: number, y0: number, x1: number, y1: number)
(x0,y0) ссылки (x1,y1) для градиентов. Следующие от (0,0) до (canvas.width,0) — это градиент в горизонтальном направлении.
addColorStop(offset: number, color: string): void;
Он используется для установки положения шкалы начального цвета градиента.
Посмотрим на эффект
const gradient = context.createLinearGradient(0, 0, canvas.width,0);
gradient.addColorStop(0 ,"rgb(100,200,155)")
gradient.addColorStop(0.5,"rgb(200,120,155)")
gradient.addColorStop(1.0,"rgb(200,220,255)")
context.fillStyle = gradient
Следующее (0,0) to (canvas.width, canvas.height) — это градиент в диагональном направлении Давайте посмотрим на эффект
Линейный градиент в любом направлении
const gradient = context.createLinearGradient(0, 0, canvas.width,canvas.height);
Суммируйте определение направления цвета градиента через направление соединения (x0, y0) и (x1, y1). Используйте addColorStop для масштабирования начального диапазона значений цвета градиента.
радиальный градиент
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number)
Радиус r0 и r1 больше, поэтому градиент будет выполняться от направления к меньшему радиусу, а не изнутри наружу или снаружи внутрь.
//region 4.径向渐变色
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width=1000;
canvas.height=500;
//绘制的对象获取
const context = canvas.getContext("2d");
//Radial(径向)
//createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number)
const gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 50, canvas.width / 2, canvas.height / 2, 20);
gradient.addColorStop(0 ,"rgb(100,200,155)")
gradient.addColorStop(0.8,"rgb(200,120,155)")
gradient.addColorStop(1.0,"rgb(00,120,105)")
context.fillStyle = gradient
//设置填充的区域为巨形 fillRect(x: number, y: number, w: number, h: number)
context.fillRect(0,0,canvas.width,canvas.height);
//endregion
Радиус от 50 до 20 Градиент снаружи внутрьПроцесс выглядит следующим образом
Радиус от 20 до 50 Градиент — изнутри наружуПроцесс выглядит следующим образом
Это все, что касается градиентов... случай будет полностью использовать градиенты. Из-за проблем со временем, только градиентный случай не будет записан.
В-третьих, трансформация полотна
Холст можно трансформировать с помощью перемещения, поворота, масштабирования, наклона и т. д., что может сделать процесс рисования более эффективным с меньшими усилиями.
По умолчанию системой координат холста является верхний левый угол, мы можем рисовать в координатах (0,0) до (100,100) и соединять линию. Следующий код и эффект:
context.beginPath() означает запуск нового пути, следующее заполнение изменит только содержимое этого пути.
context.moveTo(x, y) начальная точка пути
context.lineTo(x, y) ссылается на следующую точку
context.strokeStyle = градиент устанавливает цвет незамкнутого контура
context.stroke() путь представляет собой строку
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>canvas_change</title>
</head>
<body>
<canvas id="canvas"/>
</body>
<script>
//region 1.变换traslate
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 100;
canvas.height = 100;
//绘制的对象获取
const context = canvas.getContext("2d");
//此段路径绘制开始
context.beginPath()
context.moveTo(0, 0)
context.lineTo(100, 100)
//设置线的颜色为渐变色
const gradient = context.createLinearGradient(
0,
0, canvas.width, canvas.height);
gradient.addColorStop(0, "rgb(100,200,155)")
gradient.addColorStop(0.8, "rgb(200,120,155)")
gradient.addColorStop(1.0, "rgb(00,120,105)")
context.strokeStyle = gradient
//画不是闭合区域 fill是闭合区域
context.stroke()
</script>
</html>
Перевод холста[pan]
Можно увидеть, что наши обычные электронные диаграммы и другие диаграммы имеют систему координат, и наша система координат по умолчанию находится в верхнем левом углу. Наиболее распространенные системы координат находятся не в верхнем левом углу. Может быть более трудоемко рисовать, используя верхний левый угол в качестве точки. Больше всего мы надеемся, что (0,0) — это то относительное положение, которое нам нужно, поэтому со многими вещами легко справиться.
линейный график
Выше мы научились рисовать линии. Затем мы рисуем систему координат по умолчанию и рисуем окружность с радиусом 50 в верхнем левом углу центра по умолчанию.
<script>
//region 1.变换traslate
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 400;
canvas.height = 200;
//绘制的对象获取
const context = canvas.getContext("2d");
//设置线的颜色为渐变色
const gradient = context.createLinearGradient(
0,
0, canvas.width, canvas.height);
gradient.addColorStop(0, "rgb(100,200,155)")
gradient.addColorStop(0.8, "rgb(200,120,155)")
gradient.addColorStop(1.0, "rgb(00,120,105)")
context.strokeStyle = gradient
//绘制X轴开始
context.beginPath()
context.moveTo(0, 0)
context.lineTo(canvas.width, 0)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
//绘制Y轴
context.beginPath()
context.moveTo(0, 0)
context.lineTo(0, canvas.height)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
context.beginPath()
//绘制在圆心绘制圆圈
context.arc(0, 0, 50, 0, Math.PI * 2, true);
context.closePath()
//画不是闭合区域 fill是闭合区域
context.fillStyle = gradient
context.fill()
</script>
Затем я хочу поместить дальнюю точку координат в середину холста и добавить преобразование перемещения перед рисованием. Мы видим, что координатная ось окружности рисуется с центром холста в качестве точки окружности в процессе рисования.Конечно, вы можете перевести это как угодно.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>canvas_change</title>
</head>
<body>
<canvas id="canvas"/>
</body>
<script>
//region 1.变换traslate
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 400;
canvas.height = 200;
//绘制的对象获取
const context = canvas.getContext("2d");
//设置线的颜色为渐变色
const gradient = context.createLinearGradient(
0,
0, canvas.width, canvas.height);
gradient.addColorStop(0, "rgb(100,200,155)")
gradient.addColorStop(0.8, "rgb(200,120,155)")
gradient.addColorStop(1.0, "rgb(00,120,105)")
context.fillStyle = "rgba(100,200,155,0.2)"
context.fillRect(0, 0, canvas.width, canvas.height);
context.strokeStyle = gradient
//平移画布
context.translate(canvas.width/2,canvas.height/2)
//绘制X轴开始
context.beginPath()
context.moveTo(0, 0)
context.lineTo(canvas.width, 0)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
//绘制Y轴
context.beginPath()
context.moveTo(0, 0)
context.lineTo(0, canvas.height)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
context.beginPath()
//绘制在圆心绘制圆圈
context.arc(0, 0, 50, 0, Math.PI * 2, true);
context.closePath()
//画不是闭合区域 fill是闭合区域
context.fillStyle = "rgb(200,120,155)"
context.fill()
</script>
</html>
Поворот холста [повернуть]
Сначала мы угадываем поворот холста, а потом идем доказывать его правильность. Сначала нарисуйте линию, затем поверните холст на 10 градусов и снова нарисуйте ту же линию. Сравнение до и после рисования выглядит следующим образом:
//旋转画布
context.rotate(Math.PI/180*10)
Масштаб холста [увеличение]
Canvas Canvas может преобразовывать систему координат чертежа в нужную нам систему координат с помощью масштабирования (float sx, float sy). Например, система координат по умолчанию выглядит следующим образом:
Система координат у меня в голове не такая, а вот такая:
Далее мы хотим получить систему координат, подобную 2 ниже.
Отражение по оси x эквивалентно canvas.scale(1, -1), отражение по оси Y эквивалентно canvas.scale(-1, 1), отражение по оси эквивалентно canvas.scale(- 1, -1)
Анализируя вторую систему координат рисунка, можно увидеть, что точка находится в левом нижнем углу. Ось Y направлена положительно вверх, а ось X направлена вправо положительно по сравнению с верхним левым углом системы координат по умолчанию, ось Y направлена в противоположном направлении. В настоящее время мы можем использовать canvas.scale(1,-1), чтобы отразить преобразование, а затем перевести его вниз.
Масштаб зеркала (1,-1) по оси X
Просто переведите canvas.height вниз --- обратите внимание, что система координат положительна вверх после того, как система координат имеет масштаб (1,-1), и -canvas.height требуется для перевода внизраздел кода
<script>
//region 1.变换rote
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 400;
canvas.height = 200;
//绘制的对象获取
const context = canvas.getContext("2d");
//设置线的颜色为渐变色
const gradient = context.createLinearGradient(
0,
0, canvas.width, canvas.height);
gradient.addColorStop(0, "rgb(100,200,155)")
gradient.addColorStop(0.8, "rgb(200,120,155)")
gradient.addColorStop(1.0, "rgb(00,120,105)")
context.fillStyle = "rgba(100,200,155,0.2)"
context.fillRect(0, 0, canvas.width, canvas.height);
context.strokeStyle = gradient
//沿x轴镜像变换必须明白最重要的一点,这时候y坐标系向下为正,经过下面scale(1,-1)y轴坐标系乡下为负。
context.scale(1,-1)
//向下平移,注意这时候乡下是负方向哦
context.translate(0,-canvas.height)
//绘制X轴开始
context.beginPath()
context.moveTo(0, 0)
context.lineTo(canvas.width, 0)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
//绘制Y轴
context.beginPath()
context.moveTo(0, 0)
context.lineTo(0, canvas.height)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
context.beginPath()
//绘制线
context.moveTo(0,0);
context.lineTo(100,100);
context.closePath()
//画不是闭合区域 fill是闭合区域
context.strokeStyle = "rgb(200,120,155)"
context.stroke()
</script>
Ну вот мы и научились преобразованию системы координат, думаю каждый должен подумать такую простую вещь, не так ли? Конечно, преобразование координат имеет большое удобство и функции упрощения.Мы будем идти все глубже и глубже, и преобразование холста определенно сделает вас более эффективным с меньшими усилиями.
Четвертый, рукописный кейс ECharts
1. Линейный график
следующееОфициальный первый случай ECharts: Все они состоят из текста и различных линий и окружностей, но многие из них неразрывно связаны с нашей трансформацией координат.Давайте шаг за шагом проанализируем важность трансформации холста.
Анализировать процесс рисования.
1. Преобразуйте систему координат - привнесите удобство в работу
2. Нарисуйте линии, параллельные оси X.
3. Нарисуйте текст
4. Рисуем полилинии и круги
1. Преобразуйте систему координат - привнесите удобство в работу
Мы анализируем приведенное выше изображение, в основном левый нижний угол является центром координатного круга, чтобы нарисовать всю линейную диаграмму. Но наша система координат по умолчанию находится в верхнем левом углу, поэтому мы можем затем преобразовать точку системы координат в нижний левый угол.В приведенном выше случае мы видим, что есть определенное расстояние от дна и координаты для рисования текста.Мы установите расстояние ниже 50 на 40 влево.
//region 1.变换rote
const marginBootom = 50;
const marginLeft = 40;
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 400;
canvas.height = 200;
//绘制的对象获取
const context = canvas.getContext("2d");
//渐变
context.strokeStyle = "rgb(0,0,0,1)"
context.lineWidth=0.2
//沿x轴镜像对称变换画布
context.scale(1, -1)
//向下平移画布-marginBootom的高度
context.translate(marginLeft, -canvas.height+marginBootom)
Система координат в это время такая.Для удобства демонстрации и наблюдения я вставлю сюда систему координат. Далее мы начинаем более поздний процесс рисования.
2. Нарисуйте линии, параллельные оси X.
Нужно ли нам вычислять начальную и конечную точки каждой линии для этой линии, параллельной оси X? так хлопотно? Конечно, трансформация в холст очень хорошо решает эту проблему. Наш холст имеет состояние, и каждый раз состояние можно сохранить или вернуть в предыдущее состояние. Так: Рисуем нижнюю линию.
Затем мы можем нарисовать эту линию, сдвигая систему координат вверх на фиксированную высоту каждый раз, когда мы преобразуем систему координат в направление оси Y. Многократное рисование формирует несколько сегментов линий, параллельных оси X.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line</title>
</head>
<body>
<canvas id="canvas"/>
<script>
//region 1.变换rote
const marginBootom = 50;
const marginLeft = 40;
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 400;
canvas.height = 300;
//绘制的对象获取
const context = canvas.getContext("2d");
//渐变
context.strokeStyle = "rgb(0,0,0,1)"
context.lineWidth=0.08
//沿x轴镜像对称变换画布
context.scale(1, -1)
//向下平移画布-marginBootom的高度
context.translate(marginLeft, -canvas.height+marginBootom)
//保存好当前画布的状态。因为我们的圆心在左下角,我们还需要返回到这个远点进行其他操作。
context.save()
const heightOfOne=30
//绘制X轴开始
for(let i=0; i<7; i++){
context.beginPath()
context.moveTo(0, 0)
context.lineTo(canvas.width, 0)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
context.translate(0,heightOfOne)
}
context.restore()
</script>
</body>
</html>
3. Нарисуйте масштаб
Точно так же мы делим ось X на 7 равных частей, и нам нужно нарисовать шкалу без равных частей. код показывает, как показано ниже
//绘制刻度开始
context.save()
context.lineWidth=0.2
for(let i=0; i<8; i++){
context.beginPath()
context.moveTo(0, 0)
context.lineTo(0, -5)
context.closePath()
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
context.translate(widthOfOn,0)
}
context.restore()
Нарисуйте текст под осью X
Если очень точно, здесь может быть задействовано измерение слов. Конечно, мы должны быть точными.Из-за проблем со временем я не буду подробно объяснять здесь подробный API измерения отрисовки текста. В коде есть пояснения.
context.fillText("text",x,y); означает отрисовку текста в (x,y) позиции
//x轴绘制文字数组
const xText = new Array("Mon", "Tue", "Wed","Thu","Fir","Sat","Sun");
for(let i=0; i<xText.length; i++){
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
if(i===0){
//分析之后第一次移动了单位长度的一半。后面的每次都平移一个刻度长度,坐标圆心就平移到了每个刻度的中间。y轴向下平移了5个像素。这样就和X轴不会重合。
context.translate(widthOfOn/2,-5)
}else{
context.translate(widthOfOn,0)
}
context.fillText(xText[i],0,0);
}
context.restore()
В этот момент мы обнаруживаем, что шрифт зеркально отражается по оси X. Поскольку нижний правый угол по умолчанию является положительным направлением, мы преобразуем верхний правый угол в положительное направление. Поэтому прежде чем рисовать здесь, нам нужно восстановить систему координат.
//x轴绘制文字数组
context.save()
const xText = new Array("Mon", "Tue", "Wed","Thu","Fir","Sat","Sun");
//这里沿着X轴镜像对称变换。那么Y轴向下为正,X没变向右为正。
context.scale(1,-1)
for(let i=0; i<xText.length; i++){
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
if(i===0){
//分析之后第一次移动了单位长度的一半。后面的每次都平移一个刻度长度,坐标圆心就平移到了每个刻度的中间。y轴向下平移了5个像素。这样就和X轴不会重合。
context.translate(widthOfOn/2,15)
}else{
context.translate(widthOfOn,0)
}
context.fillText(xText[i],0,0);
}
//还原到远点为左下角状态。
context.restore()
Нарисуйте текст слева от оси Y
Тебе все еще нужен мой ББ...
//保存左下角为坐标圆点状态。
context.save()
context.scale(1,-1)
context.translate(-20,0)
context.font = "7pt Calibri";
//Y轴左边绘制文字
for(let i=0; i<7; i++){
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
context.fillText((50*i).toString(),0,0);
context.translate(0,-heightOfOne)
}
context.restore()
Шрифт выглядит так, как будто рисунок выше был нарисован, но по сравнению с исходным изображением наш текст не находится в середине каждой шкалы. Синяя точка на картинке выше. На картинке ниже то, что мы нарисовали. Ниже приводится сравнение случая: среднее положение текста, который мы рисуем, не является средним положением единичной шкалы. Нам просто нужно рассчитать ширину текста. Затем нарисуйте текст X минус половина ширины сиденья. Подумайте, как это просто?
context.measureText("текст");измерить текст
context.fillText(xText[i],-textWidth.width/2,0);
//x轴绘制文字数组
context.save()
const xText = new Array("Mon", "Tue", "Wed","Thu","Fir","Sat","Sun");
//这里沿着X轴镜像对称变换。那么Y轴向下为正,X没变向右为正。
context.scale(1,-1)
context.font = "7pt Calibri";
for(let i=0; i<xText.length; i++){
//画不是闭合区域 fill是闭合区域
context.stroke()
//每次绘制完之后继续往上平移
if(i===0){
//分析之后第一次移动了单位长度的一半。后面的每次都平移一个刻度长度,坐标圆心就平移到了每个刻度的中间。y轴向下平移了5个像素。这样就和X轴不会重合。
context.translate(widthOfOn/2,15)
}else{
context.translate(widthOfOn,0)
}
const textWidth = context.measureText(xText[i]);
context.fillText(xText[i],-textWidth.width/2,0);
}
//还原到远点为左下角状态。
context.restore()
Чувствуете волну того же текста по оси Y? Перестаньте быть многословным.
4. Рисуем полилинии и круги
Есть небольшая проблема со всем рисованием, независимо от того, какой конец должен отображать наши фактические данные в нашу систему координат. Это всего лишь простой расчет. Позвольте мне упомянуть его здесь. Например, наша система координат рассчитывается в соответствии с шириной холста, то есть пикселями. В то время наши фактические данные могут быть произвольными данными. Итак, нам нужно сопоставить нашу систему координат в соответствии с данными. Данные полилинии в этом случае выглядят так:
Определите класс для хранения координат, первый раз использовать интерфейсный объект странно...
const Point = {
createNew: function (x,y) {
const point = {};
point.x = x;
point.y = y;
return point;
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line</title>
<script type="text/javascript" src="js/canvas..js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
//region 1.变换rote
const marginBootom = 50;
const marginLeft = 40;
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 500;
canvas.height = 300;
//绘制的对象获取
const context = canvas.getContext("2d");
//渐变
context.strokeStyle = "rgb(0,0,0,1)"
context.lineWidth = 0.09
//沿x轴镜像对称变换画布
context.scale(1, -1)
//向下平移画布-marginBootom的高度
context.translate(marginLeft, -canvas.height + marginBootom)
//绘制X轴和刻度
drawX(context)
//绘制文字
drawText(context)
//绘制折线和圆
drawLine(context)
//绘制圆
drawCircle(context)
</script>
</body>
</html>
файл canvas.js
//绘制折线
function drawLine(context) {
//绘制折线段
const widthOfOn = (canvas.width - marginLeft) / 7
const danweiHeight=35/50;//每个数字占用的实际像素高度
const point01 = Point.createNew(widthOfOn/2,150*danweiHeight);
const point02 = Point.createNew(widthOfOn/2+widthOfOn,250*danweiHeight);
const point03 = Point.createNew(widthOfOn/2+widthOfOn*2,225*danweiHeight);
const point04 = Point.createNew(widthOfOn/2+widthOfOn*3,211*danweiHeight);
const point05 = Point.createNew(widthOfOn/2+widthOfOn*4,140*danweiHeight);
const point06 = Point.createNew(widthOfOn/2+widthOfOn*5,148*danweiHeight);
const point07 = Point.createNew(widthOfOn/2+widthOfOn*6,260*danweiHeight);
const points = [point01, point02, point03, point04, point05, point06, point07];
context.save();
context.beginPath();
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
}
context.strokeStyle="rgb(93,111,194)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.restore();
}
//绘制圆圈
function drawCircle(context) {
const widthOfOn = (canvas.width - marginLeft) / 7
const danweiHeight=35/50;//每个数字占用的实际像素高度
const point01 = Point.createNew(widthOfOn/2,150*danweiHeight);
const point02 = Point.createNew(widthOfOn/2+widthOfOn,250*danweiHeight);
const point03 = Point.createNew(widthOfOn/2+widthOfOn*2,225*danweiHeight);
const point04 = Point.createNew(widthOfOn/2+widthOfOn*3,211*danweiHeight);
const point05 = Point.createNew(widthOfOn/2+widthOfOn*4,140*danweiHeight);
const point06 = Point.createNew(widthOfOn/2+widthOfOn*5,148*danweiHeight);
const point07 = Point.createNew(widthOfOn/2+widthOfOn*6,260*danweiHeight);
const points = [point01, point02, point03, point04, point05, point06, point07];
context.save();
context.beginPath();
for (let index = 0; index < points.length; index++) {
context.beginPath();
context.arc(points[index].x,points[index].y,3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = 'rgb(100,255,255)';
context.shadowBlur = 5;
context.shadowColor = 'rgb(100,255,255)';
context.fill()
}
canvas.restore();
}
На этом этапе, если вы пойдете шаг за шагом и посмотрите на API для поиска Baidu, который вы не понимаете, я считаю, что если вы практикуете по крайней мере три преобразования, каждое из которых использует холст, то вы достигли хорошего уровня. настройки.Конечно, для всех видов шоу-операций мы можем узнать больше贝塞尔曲线
подожди и动画
добавлять手势
Ждать.
гладкая линейная диаграмма
Сегодня я впервые столкнулся с настройкой HTML5. На самом деле, настройка каждого конца представляет собой инкапсуляцию API, основанную на базовом рендеринге и рисовании. Хорошая платформа или язык должны иметь полный API. На мой взгляд , причина, по которой H5 имеет такую библиотеку, как ECharts. API можно усовершенствовать, поэтому я очень уверен в этой главе.
-
曲线
Он часто появляется в виде пользовательских значков во время разработки, а рисование кривой обучения может сделать ваше программное обеспечение более удобным.创造性
и无穷的魅力
.
1. Понимание и понимание кривой
- Поскольку Android ранее писал некоторые общие положения и пояснения, вот код и понимание Android, а проблему со временем можно увидеть здесь.
曲线常见的API
1.一阶曲线
2.二阶曲线
3.三阶曲线
Мы узнали самые разные直线
,圆
,椭圆
,正玄
...曲线
Подождем соответствующих уравнений системы координат, а затем просмотрим наши уравнения для прямых и кривых.
- Первым шагом является определение класса для создания новой системы координат, и экран можно поворачивать и отображать горизонтально.
package com.example.android_draw.view
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
/**
*
* ┌─────────────────────────────────────────────────────────────┐
* │┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐│
* ││Esc│!1 │@2 │#3 │$4 │%5 │^6 │&7 │*8 │(9 │)0 │_- │+= │|\ │`~ ││
* │├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤│
* ││ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │{[ │}] │ BS ││
* │├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤│
* ││ Ctrl │ A │ S │ D │ F │ G │ H │ J │ K │ L │: ;│" '│ Enter ││
* │├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤│
* ││ Shift │ Z │ X │ C │ V │ B │ N │ M │< ,│> .│? /│Shift │Fn ││
* │└─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴──┬┴───┴┬──┴┬─────┴───┘│
* │ │Fn │ Alt │ Space │ Alt │Win│ HHKB │
* │ └───┴─────┴───────────────────────┴─────┴───┘ │
* └─────────────────────────────────────────────────────────────┘
* 版权:渤海新能 版权所有
*
* @author feiWang
* 版本:1.5
* 创建日期:2/8/21
* 描述:Android_Draw
* E-mail : 1276998208@qq.com
* CSDN:https://blog.csdn.net/m0_37667770/article
* GitHub:https://github.com/luhenchang
*/
class LHC_Cubic_View @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
init {
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
}
}
Сетка и оси координат проведены для удобства наблюдения и черчения. Считаю, что операция трансформации холста, которую я изучил в предыдущей статье, освоена, код координатной оси сетки объяснять не буду, смотрите картинку.
1. Уравнения отображаются в системе координат
Помните, когда мы были в средней школеY(x)=ax+b
уравнение прямой линии. Давайте посмотрим, как это уравнение отображается на изображение в системе координат.
Сначала определите функциюy=2x-80
Получите набор точек, чтобы увидеть эффект, мы рисуем, когда x четно, а затем рисуем точки. код показывает, как показано ниже:
private var number=0..420
//直线方程y=2x-80
private fun drawzxLine(canvas: Canvas) {
pointList= ArrayList()
//绘制方程式y=10x+20
val gPaint = getPaint()
number.forEach { t ->
val point=PointF()
if (t%2==0) {//x轴偶数点进行绘制
point.x = t.toFloat()
point.y = 2f * t - 80
pointList.add(point)
canvas.drawPoint(point.x, point.y, gPaint)
}
}
}
1. То же самое уравнение окружности, уравнение эллипса и т. д. можно преобразовать в систему координат таким образом.
2.Представленная кривая представляет собой окружность с O (a, b) в центре и r в качестве радиуса.
3. Например: (х-10)2+(y-10)2=1602Делаем отображение в систему координат.
- Решение уравнения должно быть в порядке. Преобразуем в наше знакомое уравнение:
(x-10)2+(y-10)2=1602
1. Сменить предмет (y-10)2=1602-(x-10)2
2. Квадратный корень преобразуется в математическое уравнение функции следующим образом:
После квадрата идут положительные и отрицательные значения, на которые нужно обратить внимание
- y=sqrt(160.0.pow(2.0).toFloat() - ((point.x - 10).toDouble()).pow(2.0)).toFloat() + 10
- y=-sqrt(160.0.pow(2.0).toFloat() - ((pointDown.x - 10).toDouble()).pow(2.0)).toFloat() + 10
//绘制圆圈
number.forEach { t ->
val point = PointF()
val pointDown = PointF()
//(x-10)2+(y-10)2=1602
point.x = t.toFloat()
pointDown.x = t.toFloat()
//y计算应该不用我说吧。
point.y =
sqrt(160.0.pow(2.0).toFloat() - ((point.x - 10).toDouble()).pow(2.0)).toFloat() + 10
pointDown.y = -sqrt(
160.0.pow(2.0).toFloat() - ((pointDown.x - 10).toDouble()).pow(2.0)
).toFloat() + 10
canvas.drawPoint(point.x, point.y, gPaint)
canvas.drawPoint(pointDown.x, pointDown.y, gPaint)
}
2. Кривая Безье
Благодаря вышеизложенному мы обнаружили, что все функции могут быть сопоставлены один к одному с чертежом системы координат, конечно贝塞尔曲线
Есть и формулы. Есть следующие:
线性贝塞尔曲线
- Для заданных точек P0, P1 линейная кривая Безье — это просто прямая линия между двумя точками. Эта линия дается:
二次方贝塞尔曲线
- Путь квадратичной кривой Безье прослеживается функцией B(t) по точкам P0, P1, P2:
三次方贝塞尔曲线
Четыре точки P0, P1, P2 и P3 определяют кубическую кривую Безье на плоскости или в трехмерном пространстве. Кривая начинается от P0 до P1 и идет от P2 до P3. Обычно не через P1 или P2, формула следующая:
Конечно, нативный слой на стороне Android инкапсулировал этот метод,二次方贝塞尔曲线
и三次方贝塞尔曲线
, разумеется, известную функцию можно инкапсулировать.
在Android端提供了二阶和三阶
`二次方贝塞尔曲线`:
public void quadTo(float x1, float y1, float x2, float y2)
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
`三次方贝塞尔曲线`:
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
Далее мы рисуем кривую второго порядка, а контрольная точка может перемещаться с помощью жеста и нажатия соответствующего движения экрана.手势坐标系和屏幕坐标系的映射转换
Ломаная верхней секцииЭто очень ясно внутри, поэтому я не буду объяснять это здесь.
- quadTo(float x1, float y1, float x2, float y2)
//记录移动的canvas画布坐标,不是手势坐标,由手势坐标转换为canvas坐标进行刷新
private var moveX: Float = 160f
private var moveY: Float = 160f
private fun drawQuz(canvas: Canvas) {
controllRect = Rect(
(moveX - 30f).toInt(),
(moveY + 30f).toInt(),
(moveX + 30).toInt(),
(moveY - 30f).toInt()
)
val quePath = Path()
canvas.drawCircle(0f, 0f, 10f, getPaintCir(Paint.Style.FILL))
canvas.drawCircle(320f, 0f, 10f, getPaintCir(Paint.Style.FILL))
//第一个点和控制点的连线到最后一个点链线。为了方便观察
val lineLeft = Path()
lineLeft.moveTo(0f, 0f)
lineLeft.lineTo(moveX, moveY)
lineLeft.lineTo(320f, 0f)
canvas.drawPath(lineLeft, getPaint(Paint.Style.STROKE))
//第一个p0处画一个圆。第二个p1处画一个控制点圆,最后画一个。
canvas.drawCircle(moveX, moveY, 10f, getPaintCir(Paint.Style.FILL))
quePath.quadTo(moveX, moveY, 320f, 0f)
canvas.drawPath(quePath, getPaint(Paint.Style.STROKE))
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
ACTION_DOWN,
ACTION_MOVE -> {
//在控制点附近范围内部,进行移动
Log.e("x=", "onTouchEvent: (x,y)"+(event.x - width / 2).toInt()+":"+(-(event.y - height / 2)).toInt())
//将手势坐标转换为屏幕坐标
moveX = event.x - width / 2
moveY = -(event.y - height / 2)
invalidate()
}
}
return true
}
На изображении выше контрольную точку можно перетаскивать, а кривая между начальной и конечной точками деформируется вместе с контрольной точкой.控制点靠近那一侧弧度的凸起就偏向那一侧
, Достаточно иметь предварительное представление об этом законе и постоянно корректировать контрольные точки на практике под свои нужды. Но на приведенном выше рисунке мы обнаружили, что радиана недостаточно для круга, а радиан можно хорошо подогнать в функции третьего порядка. Далее рассмотрим функцию третьего порядка
三阶曲线
- public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
Аналогично рисуем кривые третьего порядка в системе координат.
Чтобы хорошо увидеть эффект, на этот раз у нас есть точный контроль, мы можем перетаскивать任意
мы хотим перетащить控制点
Взгляните на нашу кривую третьего порядка. В предыдущей главе полилиния对于手势配合Rect的contains方法可以进行局部的点击
, Конечно, перетаскивание не проблема.
Как показано ниже: Нам нужно только нарисовать рядом с контрольной точкой距形包裹住控制点
, проведите жест新控制点
и对应的距形
Вот и все.
На данный момент, я думаю, мы должны приблизительно понять кривые второго и третьего порядка для радианов.大致方向控制
Ну давай же. Как вы думаете, это конец? Затем запустите формальное приложение кривой входа.
Графический анализ
4. Спасение кривой третьего порядка
когда<Как показано на рисунке 1 выше, найдите координату средней точки нижней части контрольной точки по оси x x+40px, верхней части x-40px, ось y также можно отрегулировать, чтобы сделать плавность, нижнюю часть контрольной точки y-40x, верхняя часть y+40.
1. Получить координаты средней точки (,знак равно+)/2,(+)/2)
2.прибытьКоординаты между =((+)/2,(+)/2)
3.прибытьКоординаты между =((+)/2,(+)/2)
когда>Как показано выше на рисунке 2. Найдите координату средней точки верхней части оси x +40px, нижней части x-40px, ось y также можно отрегулировать, ось y также можно отрегулировать, чтобы сделать гладкость Верхняя часть контрольной точки y+40x, нижняя часть y- 40.
1. Получить координаты средней точки (,знак равно+)/2,(+)/2)
2.прибытьКоординаты между =((+)/2,(+)/2)
3.прибытьКоординаты между =((+)/2,(+)/2)
/**
* 绘制曲线
* @param context
*/
function drawQuaraticLine(context) {
//绘制折线段
const widthOfOn = (canvas.width - marginLeft) / 7
const danweiHeight=35/50;//每个数字占用的实际像素高度
const point01 = Point.createNew(widthOfOn/2,150*danweiHeight);
const point02 = Point.createNew(widthOfOn/2+widthOfOn,250*danweiHeight);
const point03 = Point.createNew(widthOfOn/2+widthOfOn*2,225*danweiHeight);
const point04 = Point.createNew(widthOfOn/2+widthOfOn*3,211*danweiHeight);
const point05 = Point.createNew(widthOfOn/2+widthOfOn*4,140*danweiHeight);
const point06 = Point.createNew(widthOfOn/2+widthOfOn*5,148*danweiHeight);
const point07 = Point.createNew(widthOfOn/2+widthOfOn*6,260*danweiHeight);
const dataList = [point01, point02, point03, point04, point05, point06, point07];
context.save();
context.beginPath();
context.lineTo(point01.x,point01.y)
//500=grid_width-40 每个单位的长度的=像素长度
const danweiX = widthOfOn;
const grid_width=widthOfOn;
const xMoveDistance=20
const yMoveDistance=30
for (let index = 0;index < dataList.length-1;index++) {
if (dataList[index] === dataList[index + 1]) {
context.lineTo(danweiX*(index+1),0)
} else if(dataList[index] < dataList[index + 1]){//y1<y2情况
const centerX=(grid_width * index + grid_width * (1 + index)) / 2
const centerY=(dataList[index].y + dataList[index + 1].y) / 2
const controX0=(grid_width * index+centerX)/2
const controY0=(dataList[index].y+centerY)/2
const controX1=(centerX+ grid_width * (1 + index))/2
const controY1=(centerY+dataList[index+1].y)/2
context.bezierCurveTo(controX0+xMoveDistance,controY0-yMoveDistance,controX1-xMoveDistance,controY1+yMoveDistance,grid_width * (1 + index),dataList[index + 1].y)
}else{
const centerX=(grid_width * index + grid_width * (1 + index)) / 2
const centerY=(dataList[index].y + dataList[index + 1].y) / 2
const controX0=(grid_width * index+centerX)/2
const controY0=(dataList[index].y+centerY)/2
const controX1=(centerX+ grid_width * (1 + index))/2
const controY1=(centerY+dataList[index+1].y)/2
context.bezierCurveTo(controX0+xMoveDistance,controY0+yMoveDistance,controX1-xMoveDistance,controY1-yMoveDistance,grid_width * (1 + index),dataList[index + 1].y)
}
}
context.strokeStyle="rgb(93,111,194)"
context.lineWidth=2
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.restore();
}
Из-за нехватки времени параметры детально не настраивались, конечно, зазор между осью X слишком мал, поэтому выглядит несуразно. Если у вас есть время, вы можете прочитать мой предыдущийПользовательская кривая AndroidВолна блогов
3. Заполненная линейная диаграмма
Мы уже делали полилинии и кривые, но как насчет следующей заливки? Как сделать более нахальную операцию? Мы будем исследовать дальше.
Делаем на прежней основе, можем нарисовать замкнутый многоугольник для заливки на прежней основе.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line</title>
<script type="text/javascript" src="js/canvas..js"></script>
<script type="text/javascript" src="js/canvas_fill.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
//region 1.变换rote
const marginBootom = 50;
const marginLeft = 40;
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 500;
canvas.height = 300;
//绘制的对象获取
const context = canvas.getContext("2d");
//渐变
context.strokeStyle = "rgb(0,0,0,1)"
context.lineWidth = 0.09
//沿x轴镜像对称变换画布
context.scale(1, -1)
//向下平移画布-marginBootom的高度
context.translate(marginLeft, -canvas.height + marginBootom)
//绘制X轴和刻度
drawX(context)
//绘制文字
drawText(context)
//绘制折线和圆
drawFillLine(context)
//绘制圆
drawCircle(context)
</script>
</body>
</html>
Введите код
function drawFillLine(context) {
//绘制折线段
const widthOfOn = (canvas.width - marginLeft) / 7
const danweiHeight=35/50;//每个数字占用的实际像素高度
const point00 = Point.createNew(0,150*danweiHeight);
const point01 = Point.createNew(widthOfOn/2,150*danweiHeight);
const point02 = Point.createNew(widthOfOn/2+widthOfOn,250*danweiHeight);
const point03 = Point.createNew(widthOfOn/2+widthOfOn*2,225*danweiHeight);
const point04 = Point.createNew(widthOfOn/2+widthOfOn*3,211*danweiHeight);
const point05 = Point.createNew(widthOfOn/2+widthOfOn*4,140*danweiHeight);
const point06 = Point.createNew(widthOfOn/2+widthOfOn*5,148*danweiHeight);
const point07 = Point.createNew(widthOfOn/2+widthOfOn*6,260*danweiHeight);
const points = [point00,point01, point02, point03, point04, point05, point06, point07];
context.save();
context.beginPath();
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
}
context.strokeStyle="rgb(93,111,194)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.beginPath();
//绘制闭环多边形
context.moveTo(0,0)
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
}
context.lineTo(points[points.length-1].x,0);
context.lineTo(0,0);
context.closePath();
context.fillStyle="rgba(93,111,194,0.5)"
context.lineWidth=3
context.shadowBlur = 5;
context.fill();
context.restore();
}
Использование градиентных цветов придает ему больше характера и очарования. Я, конечно, не стандартный дизайнер, красота не в нас, а в дизайне, хороший дизайн не должен быть таким уродливым, как я, правда?
3. Несколько линейных диаграмм
Есть много линейных диаграмм, как показано ниже, у нас есть одна, но нельзя получить более одной? Требуется только та же операция, но набор данных другой.
Из-за нехватки времени мы работаем напрямую на основе предыдущего
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line</title>
<script type="text/javascript" src="js/canvas..js"></script>
<script type="text/javascript" src="js/canvas_fill.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
//region 1.变换rote
const marginBootom = 50;
const marginLeft = 40;
const canvas = document.getElementById("canvas");
//设置画布的宽高
canvas.width = 500;
canvas.height = 300;
//绘制的对象获取
const context = canvas.getContext("2d");
//渐变
context.strokeStyle = "rgb(0,0,0,1)"
context.lineWidth = 0.09
//沿x轴镜像对称变换画布
context.scale(1, -1)
//向下平移画布-marginBootom的高度
context.translate(marginLeft, -canvas.height + marginBootom)
//绘制X轴和刻度
drawX(context)
//绘制文字
drawText(context)
function drawMoreLine(context) {
//绘制折线段
const widthOfOn = (canvas.width - marginLeft) / 7
const danweiHeight=35/50;//每个数字占用的实际像素高度
const point01 = Point.createNew(widthOfOn/2, 200*danweiHeight);
const point02 = Point.createNew(widthOfOn/2+widthOfOn, 250*danweiHeight);
const point03 = Point.createNew(widthOfOn/2+widthOfOn*2,225*danweiHeight);
const point04 = Point.createNew(widthOfOn/2+widthOfOn*3,211*danweiHeight);
const point05 = Point.createNew(widthOfOn/2+widthOfOn*4,140*danweiHeight);
const point06 = Point.createNew(widthOfOn/2+widthOfOn*5,148*danweiHeight);
const point07 = Point.createNew(widthOfOn/2+widthOfOn*6,260*danweiHeight);
const point011 = Point.createNew(widthOfOn/2, 150*danweiHeight);
const point012 = Point.createNew(widthOfOn/2+widthOfOn, 200*danweiHeight);
const point013 = Point.createNew(widthOfOn/2+widthOfOn*2,125*danweiHeight);
const point014 = Point.createNew(widthOfOn/2+widthOfOn*3,181*danweiHeight);
const point015 = Point.createNew(widthOfOn/2+widthOfOn*4,90*danweiHeight);
const point016 = Point.createNew(widthOfOn/2+widthOfOn*5,98*danweiHeight);
const point017 = Point.createNew(widthOfOn/2+widthOfOn*6,210*danweiHeight);
const point021 = Point.createNew(widthOfOn/2, 60*danweiHeight);
const point022 = Point.createNew(widthOfOn/2+widthOfOn, 65*danweiHeight);
const point023 = Point.createNew(widthOfOn/2+widthOfOn*2,61*danweiHeight);
const point024 = Point.createNew(widthOfOn/2+widthOfOn*3,70*danweiHeight);
const point025 = Point.createNew(widthOfOn/2+widthOfOn*4,78*danweiHeight);
const point026 = Point.createNew(widthOfOn/2+widthOfOn*5,68*danweiHeight);
const point027 = Point.createNew(widthOfOn/2+widthOfOn*6,72*danweiHeight);
const points = [point01, point02, point03, point04, point05, point06, point07];
const point1s = [point011, point012, point013, point014, point015, point016, point017];
const point2s = [point021, point022, point023, point024, point025, point026, point027];
context.save();
context.beginPath();
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
}
context.strokeStyle="rgb(93,111,194)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.beginPath();
for (let index = 0; index < point1s.length; index++) {
context.lineTo(point1s[index].x,point1s[index].y);
}
context.strokeStyle="rgb(193,111,194)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.beginPath();
for (let index = 0; index < point2s.length; index++) {
context.lineTo(point2s[index].x,point2s[index].y);
}
context.strokeStyle="rgb(293,111,294)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.beginPath();
for (let index = 0; index < point2s.length; index++) {
context.lineTo(point2s[index].x,point2s[index].y-15);
}
context.strokeStyle="rgb(293,211,194)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
context.beginPath();
for (let index = 0; index < point2s.length; index++) {
context.lineTo(point2s[index].x,point2s[index].y-35);
}
context.strokeStyle="rgb(93,211,294)"
context.lineWidth=1
context.shadowBlur = 5;
context.stroke();
context.closePath();
for (let index = 0; index < points.length; index++) {
context.beginPath();
context.arc(points[index].x,points[index].y, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = 'rgb(100,255,255)';
context.shadowBlur = 10;
context.shadowColor = 'rgb(100,255,255)';
context.fill()
}
//第一个上面的圆
for (let index = 0; index < points.length; index++) {
context.beginPath();
context.arc(points[index].x,points[index].y, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "rgb(93,111,194)";
context.shadowBlur = 10;
context.shadowColor = "rgb(93,111,194)";
context.fill()
}
//第二条线上面的圆
for (let index = 0; index < point1s.length; index++) {
context.beginPath();
context.arc(point1s[index].x,point1s[index].y, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "rgb(193,111,194)"
context.shadowBlur = 10;
context.shadowColor ="rgb(193,111,194)"
context.fill()
}
//第三条线的圆
for (let index = 0; index < point2s.length; index++) {
context.beginPath();
context.arc(point2s[index].x,point2s[index].y, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "rgb(293,111,294)"
context.shadowBlur = 10;
context.shadowColor ="rgb(293,111,294)"
context.fill()
}
//四...圆
for (let index = 0; index < point2s.length; index++) {
context.beginPath();
context.arc(point2s[index].x,point2s[index].y-15, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "rgb(293,211,194)"
context.shadowBlur = 1;
context.shadowColor ="rgb(293,211,194)"
context.fill()
}
//第五条线上面的圆圈
for (let index = 0; index < point2s.length; index++) {
context.beginPath();
context.arc(point2s[index].x,point2s[index].y-35, 3, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "rgb(93,211,294)"
context.shadowBlur = 1;
context.shadowColor ="rgb(93,211,294)"
context.fill()
}
context.restore();
}
//绘制填充折线和圆
drawMoreLine(context)
</script>
</body>
</html>
4. Многострочные диаграммы заполнения
Из-за нехватки времени это последний случай. Пожалуйста, с нетерпением ждите моего буклета, чтобы в будущем увидеть лучшие случаи со спецэффектами. Я был на пути к улучшению своего письма. Надеюсь встретиться с вами как можно скорее.
анализировать
Только суперпозиция закрытых областей
//第一条线闭合区域
context.beginPath();
context.moveTo(0,0)
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
}
context.lineTo(points[points.length-1].x,0);
context.closePath();
context.fillStyle="rgba(93,111,194,0.5)"
context.lineWidth=11
context.shadowBlur = 5;
context.fill();
context.closePath();
несколько слов
//第一条线闭合区域
context.beginPath();
context.moveTo(0,0)
for (let index = 0; index < points.length; index++) {
context.lineTo(points[index].x,points[index].y);
//这里由于文字反转所以需要变换坐标系。且为了方便操作每次都将坐标圆点移动到顶点跟家方便的操作
context.save()
context.translate(points[index].x,points[index].y)
context.scale(1,-1)
context.fillText(points[index].y+"",0,-10)
//记得文字绘制完成还原坐标系,因为后面还要绘制线,不影响坐标系圆点是左下叫为圆形即可。
context.restore()
}
context.lineTo(points[points.length-1].x,0);
context.closePath();
5. Случай гистограммы
Некоторые люди в группе упомянули о написании гистограмм. Днем, когда я увидел шишек переднего плана, было действительно много людей. Было так много людей, которые написали эту гидрологию, поэтому я не мог дождаться, чтобы добавить эту гистограмму, ха-ха. Большое спасибо фронтовикам. Впервые пишу js-контент, пожалуйста, укажите и простите меня, если возникнут какие-либо проблемы.
1. Постройте ось X и деления. см. комментарии к коду
let marginLeft=80
let marginBootom=100
function translateCanvas(context) {
//画布变换到左下角。
context.translate(marginLeft,canvas.height-marginBootom)
context.scale(1,-1)
}
//绘制X线和刻度等
function drawXLine(context) {
//绘制X轴
context.beginPath();
//起始点
context.moveTo(0,0)
//X轴结束点
context.lineTo(canvas.width-marginLeft*2,0)
context.closePath();
context.strokeStyle = 'rgb(0,0,0)';
context.shadowBlur = 2;
context.lineWidth=0.1
context.shadowColor = 'rgb(100,255,255)';
context.stroke()
//绘制平行线
context.save()
//计算每一份单位宽度
let heightOne=(canvas.height-marginBootom*2)/7
for (let index=0; index<8; index++){
context.beginPath();
context.moveTo(0,0)
context.lineTo(canvas.width-marginLeft*2,0)
context.closePath();
context.strokeStyle = 'rgb(0,0,0)';
context.shadowBlur = 2;
context.lineWidth=0.1
context.shadowColor = 'rgb(100,255,255)';
context.stroke()
context.translate(0,heightOne)
}
context.restore()
//绘制刻度
context.save()
//计算每一份单位宽度
let widthOne=(canvas.width-marginLeft*2)/20
for (let index=0; index<21; index++){
context.beginPath();
context.moveTo(0,0)
context.lineTo(0,-5)
context.closePath();
context.strokeStyle = 'rgb(0,0,0)';
context.lineWidth=0.1
context.stroke()
context.translate(widthOne,0)
}
context.closePath()
context.restore()
}
Эффект следующий:
2. Нарисуйте текст под осью X. см. комментарии к коду
Здесь немного больше текста для рисования, чтобы измерять его через MeasureText.Как нарисовать текст на середину шкалы?
let xText=["北京","天津","河北","山西","四川","广东","上海","深圳","江苏","河南","山西",
"陕西","甘肃","内蒙","天塌","运城","哈尔滨","日本","台湾","香港"]
//绘制底部文字
function drawXDownText(context){
//绘制刻度
context.save()
context.scale(1,-1)
context.beginPath();
//计算每一份单位宽度
let widthOne=(canvas.width-marginLeft*2)/20
for (let index=0; index<xText.length; index++){
const textWidth = context.measureText(xText[index]);
//计算文字的文字,自己画图看看很简单就是相对位置剪来剪去
context.strokeStyle = 'rgb(111,11,111)';
context.fillText(xText[index],widthOne/2-textWidth.width/2,textWidth.emHeightAscent)
context.stroke()
context.translate(widthOne,0)
}
context.restore()
//绘制Y轴左边的文字
context.save()
//这里很重要,为了字体不反转
context.scale(1, -1)
context.translate(-30, 0)
context.font = "7pt Calibri";
let heightOne=(canvas.height-marginBootom*2)/7
//Y轴左边绘制文字
for (let i = 0; i < 8; i++) {
//画不是闭合区域 fill是闭合区域
context.stroke()
//测量文字
const textHeight = context.measureText((3000 * i).toString());
//设置文字绘制的位置
context.fillText((3000 * i).toString(), 0, textHeight.emHeightAscent / 2);
//每次绘制完之后继续往上平移
context.translate(0, -heightOne)
}
context.restore()
}
3. Нанесение данных о гигантах и зданиях
⭐️⭐️⭐️⭐️⭐️⭐️Более важный момент здесь, в области комментариев также упоминалось, как совместить фактические данные с системой координат. Например, наши фактические данные получены из десятков тысяч фоновых данных. И высота нашей системы координат ограничена 500px. По сути, простая операция заключается в том, что на количество единиц приходится фактическая высота пикселя danwei=500/максимальное значение (например, 2000). 12000*danwei — это то место, где должно быть 12000 на реальном холсте.
let datas=[[100,2800,9000],[150,2900,600],[300,12000,400],[500,13333,4000],[1300,2000,122],[111,3333,1111],[1111,2111,1111],[111,1611,222],[444,4444,333],[222,11111,2222],[2222,2235,11],[111,1345,1111],[1111,11111,2234],[1122,12223,12],[121,1665,111],[234,334,21]
,[112,12134,1211],[1212,12111,134],[124,2021,112],[1222,20345,1212],[1412,17771,1111],[111,12222,1111],[1123,121333,1111],[11112,11212,111],]
//绘制巨型等条状物
function drawRect(context) {
//绘制刻度
context.save()
context.beginPath();
//计算每一份单位宽度
let widthOne=(canvas.width-marginLeft*2)/20
let widthOneRect=widthOne/3
let heightOne=(canvas.height-marginBootom*2)/7
let danwei=heightOne/3000
for (let index=0; index<xText.length; index++){
//计算文字的文字,自己画图看看很简单就是相对位置剪来剪去
context.fillStyle = 'rgb(189,119,119)';
context.fill()
//第一个条纹
context.fillRect(0,0,widthOneRect-1,datas[index][0]*danwei)
context.fillStyle = 'rgb(19,172,172)';
//第二个条纹
context.fillRect(widthOneRect,0,widthOneRect-1,datas[index][1]*danwei)
context.fillStyle = 'rgb(111,73,142)';
//第三个条纹
context.fillRect(widthOneRect*2,0,widthOneRect-1,datas[index][2]*danwei)
context.translate(widthOne,0)
}
context.restore()
}
Я добавлю больше позже, когда у меня будет время...
5. Радар серии
Если вы последуете за мной в вышеизложенном, я думаю, вам уже не составит труда настроить его.Давайте посмотрим, что может дать нам простой пользовательский API и простой математический расчет в средней школе.
Анализ перед построением
Удобство преобразования координат в центр экрана
Нарисуйте множество скелетных линий
Как фактические данные отображаются на экране
Заполнение соединения завершено
1. Преобразование координат
Как показано выше, если точка находится в центре экрана, это будет очень удобно. Перейдите непосредственно к коду.... Непонятно видеть часть преобразования координат выше
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>雷达图</title>
</head>
<body>
<canvas id="canvas" style="background: black"></canvas>
</body>
<script>
const canvas = document.getElementById("canvas");
canvas.width = 831;
canvas.height = 706;
//绘制的对象获取
const context = canvas.getContext("2d")
context.translate(canvas.width/2,canvas.height/2)
context.scale(1,-1)
//绘制圆
context.arc(0,0,50,0,Math.PI*2,true)
context.strokeStyle="rgb(189,142,16)"
context.stroke()
</script>
</html>
2. Нарисуйте несколько сегментов линии скелета
Мы видим всего три скелетные линии, делящие экран на六等分
, мы можем просто найти уравнение трех отрезков, верно? Я верю, что вы можете понять математику средней школы.
Yx=-tan30*x
Yx= tan30*x
let y1= Math.tan(Math.PI/180*30)*(-300)
let y2= Math.tan(Math.PI/180*30)*300
context.moveTo(-300,y1)
context.lineTo(300,y2)
context.closePath();
context.strokeStyle="rgb(189,142,16)"
context.stroke()
завершенный
context.beginPath();
//左边一条骨架线段
let y1= Math.tan(Math.PI/180*30)*(-300)
let y2= Math.tan(Math.PI/180*30)*300
context.moveTo(-300,y1)
context.lineTo(300,y2)
context.strokeStyle="rgb(189,142,16)"
context.stroke()
//中间骨架线
context.moveTo(0,300)
context.lineTo(0,-300)
//右边一条骨架线段
let y11= -Math.tan(Math.PI/180*30)*(-300)
let y22= -Math.tan(Math.PI/180*30)*300
context.moveTo(-300,y11)
context.lineTo(300,y22)
context.strokeStyle="rgb(189,142,16)"
context.stroke()
context.closePath();
Расходящийся круг
for (let i = 0; i < 6; i++) {
context.beginPath();
//绘制圆
context.arc(0,0,50*(i+1),0,Math.PI*2,true);
context.strokeStyle="rgb(189,142,16)";
context.stroke();
context.closePath();
}
3. Как отображать фактические данные на экране
Точно так же радиус нашей окружности можно рассматривать как длину каждой координатной оси скелета, а наши фактические данные — это данные длины Как сопоставить число длины с каждой неправильной координатной осью скелета? Конечно, она по-прежнему неотделима от простой математики. Например, у нас есть число 250, где две белые пунктирные линии пересекаются, как показано на рисунке ниже. Наши фактические 250 представляют собой длину точки до фокальной части. Но нам нужно найти в системе координат, затем нам нужно найти виртуальные координаты (x, y) в системе координат. В той же простой математике средней школы нетрудно найти (x, y) = (длинаcson30,lenghtsin30), если вы тщательно проанализируете все координаты на каждой оси координат скелета, чтобы удовлетворить (x, y) = (длинаcson30,lenghtгрех30). Далее давайте перейдем к коду, чтобы увидеть эффект
//绘制网线填充
const datas = [[70, 100, 20, 5, 21, 99],[100, 120,50, 75, 121, 99],[117,211,259,232,190,200],[217,240,259,282,190,120]];
for (let i = 0; i < datas.length; i++) {
for (let index=0;index<datas[i].length;index++){
context.beginPath()
//右上角开始顺时针开始绘制
context.lineTo(datas[i][0]*Math.cos(Math.PI/180*30),datas[i][0]*Math.sin(Math.PI/180*30))
context.lineTo(datas[i][1]*Math.cos(Math.PI/180*30),-datas[i][1]*Math.sin(Math.PI/180*30))
context.lineTo(0,-datas[i][2])
context.lineTo(-datas[i][3]*Math.cos(Math.PI/180*30),-datas[i][3]*Math.sin(Math.PI/180*30))
context.lineTo(-datas[i][4]*Math.cos(Math.PI/180*30),datas[i][4]*Math.sin(Math.PI/180*30))
context.lineTo(0,datas[i][5])
context.fillStyle="rgba(189,142,16,0.09)"
context.fill()
context.closePath();
}
}
//绘制网线边缘线条
for (let i = 0; i < datas.length; i++) {
for (let index=0;index<datas[i].length;index++){
context.beginPath()
//右上角开始顺时针开始绘制
context.lineTo(datas[i][0]*Math.cos(Math.PI/180*30),datas[i][0]*Math.sin(Math.PI/180*30))
context.lineTo(datas[i][1]*Math.cos(Math.PI/180*30),-datas[i][1]*Math.sin(Math.PI/180*30))
context.lineTo(0,-datas[i][2])
context.lineTo(-datas[i][3]*Math.cos(Math.PI/180*30),-datas[i][3]*Math.sin(Math.PI/180*30))
context.lineTo(-datas[i][4]*Math.cos(Math.PI/180*30),datas[i][4]*Math.sin(Math.PI/180*30))
context.lineTo(0,datas[i][5])
context.strokeStyle="rgb(189,142,16)"
context.stroke()
context.closePath();
}
}
Лимит текста самородков, начнется новая глава!!!!
V. Резюме
Для меня человек, который в основном никогда не использовал мобильный терминал HTML5 и JS, может заполнить это содержимое за день. Это нелегко для фронтенд-больших.Конечно, странно то, что я также спрашивал многих фронтенд-разработчиков, а я мало знаю о настройке, и я недостаточно глубок. спрашивайте о диаграммах, ECharts всегда экспортирует часто.Возможно, у некоторых компаний пользовательский интерфейс не слишком строгий.Разработка в основном ищет аналогичные диаграммы в ECharts или дизайнер выбирает дизайн непосредственно на основе ECharts.Это может быть по разным причинам, что внутренний пользовательский интерфейс в основном, как правило, то же самое.Технологии меняются, и дизайн пользовательского интерфейса также должен быть уникальным и отличительным. Конечно, лучше иметь возможность кастомизации, не лучше ли? Когда вы сталкиваетесь с необычными конструкциями, вы можете делать все, что захотите, какая замечательная вещь.
Я думаю, что следующие диаграммы — не что иное, как гигант, радиан и т. д. плюс преобразование холста, градиент заливки, рисование текста, верно? Пришло время показать свои навыки
КонечноEChartsТам много кастомных вещей. Если вы серьезно относитесь к вводу этого текста, вы должны быть уверены в пользовательском контенте. Что касается хороших операций, конечно, жесты и анимация неразделимы, позже мы начнем жесты и анимацию, чтобы постепенно перейти кK线
, верно, линия К.