Создавайте и изменяйте SVG-графику данных от датчиков, подключенных к Интернету вещей, с помощью JavaScript.
В этой последней части серии статей о рисовании графика с данными датчиков, подключенных к Интернету вещей, пришло время поговорить о том, как создавать или изменять с помощью JavaScript рисунки в формате SVG и некоторые элементы HTML которые служат контейнером или представляют дополнительную информацию к графике.
Целевые пользователи этого руководства должны сформировать профиль электроники и компьютерного программирования. микроконтроллеры, они могут быть не знакомы с HTML, CSS o SVG; По этой причине в предыдущих частях было сделано краткое введение в язык или соответствующую технологию. В этой последней части подход немного другой, поскольку читатели наверняка знают, как программировать, возможно, что использование языка C + + что, как JavaScript, разделяет базовый синтаксис с C и его можно использовать в качестве справочника, чтобы пропустить большинство основных концепций программирования и, таким образом, сосредоточиться на различиях и конкретном использовании, которое нас интересует, для создания сенсорной графики в IoT.
Название дает ключ к первому отличию: JavaScript это язык программирования скрипт (дефис) и поэтому это интерпретированы, нет необходимости его компилировать; контекст, в котором скрипт (например, веб-браузер) будет читать, переводить и выполнять заказы. Точнее, в большинстве случаев компиляция времени выполнения (JIT), но для процесса написания кода JavaScript Нас это не касается, мы просто пишем код и он может работать.
В названии также заключена первая путаница: JavaScript не имеет ни малейшего отношения к Java. Первоначально, когда был разработан Netscape браузер назывался сначала Mocha, а затем менее запутанным LiveScript. После успешной реализации в браузерах и выхода за их пределы он был стандартизирован как ECMAScript (из ECMA-262, версия 6 на момент написания), чтобы стать нейтральным по отношению к браузерам, которые его реализуют. В настоящее время также существует стандарт ISO с версии 5, 2011 (ИСО / МЭК 16262: 2011 на момент написания статьи)
Переменные, основные типы данных и объекты в JavaScript
В отличие от того, что происходит, например, в C + +, en JavaScript тип данных не включается при объявлении переменной а также тип, связанный с переменной, не фиксирован, возможно присвоение значения другого типа на протяжении всего выполнения программы.
1 2 3 4 5 6 7 | var cosa; cosa=“texto”; console.log(typeof cosa); // Debería mostrar string en la consola cosa=123; console.log(typeof cosa); // Debería mostrar number en la consola cosa={temperatura:22,corriente:1.5}; console.log(typeof cosa); // Debería mostrar object en la consola |
В предыдущем примере была объявлена переменная «вещь» (без указания типа данных), затем присваиваются данные другого типа и с ними обращаются. typeof
тип, который JavaScript что он истолковал. Для отладки кода вы можете написать его в консоли инспектора веб-браузера (это не повлияет на представление Интернета) с помощью console.log()
.
Чтобы принудительно преобразовать данные в определенный тип, особенно текст в числовой, вы можете использовать такие функции, как parseInt()
o parseFloat()
которые преобразуются в целые числа или числа с плавающей запятой соответственно. Обратное преобразование можно выполнить с помощью String()
, хотя это вряд ли необходимо, поскольку автоматического преобразования обычно достаточно. С parseFloat()
Например, вы можете получить значение свойства веб-страницы, например ширину или высоту объекта, включающее единицы измерения; Таким образом, выражение parseFloat("50px");
в результате вернет 50, числовое значение.
En JavaScript нет различия между двойными и одинарными кавычками; Тип данных в обоих случаях string
, и каждый из них может включать другой без необходимости использования escape-кодов.
1 2 3 4 5 6 7 8 9 10 | var texto; console.log(typeof texto); // Debería mostrar string en la undefined texto=“esto es un texto”; console.log(typeof texto); // Debería mostrar string en la consola texto=‘A’; console.log(typeof texto); // Debería mostrar string en la consola texto=“esto es un ‘texto'”; console.log(typeof texto); // Debería mostrar string en la consola texto=‘”A”‘; console.log(typeof texto); // Debería mostrar string en la consola |
В предыдущем примере видно, что переменная, если она была объявлена (существует), но ей не было присвоено никакого значения, содержит неопределенный тип данных (undefined
). Неназначенный объект имеет значение null
; То есть объект существует, но не имеет ценности; переменная, которая ссылается на нее, не будет иметь typeof
undefined
Китайско object
. Объект также может быть пустым, то есть не иметь значения NULL, но не иметь никаких свойств.
к определить объект в JavaScript заключаются в фигурные скобки ({
y }
) свойства или методы, разделенные знаком двоеточия (:
) имя свойства, значение свойства и через запятую (,
) различные свойства. Более подробную информацию об этом способе выражения объекта вы можете найти в статье на Формат JSON.
Хотя вы можете использовать синтаксис, который может заставить вас думать иначе, en JavaScript Нет классов, есть прототипыТо есть, чтобы объект наследовал свойства и методы, создается другой объект (прототип), который другие (дочерние элементы) используют в качестве ссылки. Синтаксис, наиболее близкий к стилю JavaScript использовать прототип - это Object.create
хотя также возможно (а иногда и полезно) использовать new
как и в других объектно-ориентированных языках.
1 2 3 4 | var perro=new Mamifero(); // Esto funciona, pero no es exactamente el nuevo estilo JavaScript console.log(perro instanceof Mamifero); var gato=Object.create(Mamifero); // Crear un objeto usando un prototipo al estilo JavaScript console.log(Mamifero.isPrototypeOf(gato)); |
к запрос, является ли один объект экземпляром другого, если вы используете его как прототип, если вы наследуете его свойства, короче говоря, вы можете использовать instanceof
(создано с new
) Или isPrototypeOf
(создано с Object.create
), который будет иметь значение true, если объект использует прототип, и false, если нет.
Как только объект был создан с использованием другого в качестве прототипа, то есть как только объект был создан, его можно добавить новые свойства или переопределить свойства прототипа используя точечный синтаксис, как в gato.peso=2.5
.
La массивы в JavaScript Они отличаются от тех, которые вы, вероятно, знаете в C. Начнем с того, что они объявляются без необходимости указания их длины, только со знаками открывающих и закрывающих квадратных скобок ([
y ]
), компоненты могут быть разнородными (разные типы данных в одном и том же массиве), а новые элементы могут добавляться без каких-либо ограничений. Матрицы JavaScript на самом деле являются списками (коллекциями) элементов, к которым на который ссылается числовой индекс или имя. Массив может одновременно содержать числовые индексы и имена элементов, но для использования второго типа обычно используются объекты (свойства).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // Declarar matrices (arrays) var preparada=[]; // La matriz ha sido declarada pero (todavía) no contiene valores var cosas=[“silla”,“mesa”,“caja”]; // Matriz declarada con componentes formada por cadenas de texto var valores=[200,“lleno”,0.5,true,“simple”,false,false,10]; // Matriz declarada con componentes heterogéneos var ramas=[20,“abc”,[1,2,3],false,[10,20,[“uno”,“dos”]]]; // Matriz que contiene matrices var demode=new Array(10,20,30,4,3,2,1); // La sintaxis con new no es la preferida de JavaScript aunque funciona… var peligrosa=new Array(10); // …pero con el riesgo de confundir índices con elementos: la matriz peligrosa tiene 10 elementos, no un elemento de valor 10 // Acceder a los valores de la matriz preparada.push(33.33); // Añade un nuevo valor al final de la matriz console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 1 elemento console.log(cosas[0]); // Muestra en la consola el primer valor de la matriz (las matrices empiezan en el índice cero) cosas[2]=“tarro”; preparada[10]=50; // Los índices no tienen que ser consecutivos console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 11 elementos console.log(“Elemento sexto: “+preparada[5]); // undefined // Verificar si una variable es (apunta a) una matriz console.log(Array.isArray(cosas)); // Nuevas versiones de JavaScript (ECMAScript versión 5 o superior) console.log(cosas instanceof Array); // Implementaciones de JavaScript (ECMAScript) más viejas // Para esto es mejor usar objetos var frutas=[]; frutas[“peras”]=20; frutas[“manzanas”]=30; frutas[4]=10; console.log(frutas.peras); console.log(frutas[“manzanas”]); console.log(frutas[4]); console.log(frutas[3]); // undefined |
Как видно из предыдущего примера, чтобы узнать, соответствует ли переменная экземпляру массива (это объект массива), вы можете использовать instanceof
, как это уже использовалось с универсальными объектами или, в более поздних версиях JavaScript можно прибегнуть к Array.isArray()
Для доступа к элементам массива вы можете использовать его индекс (matriz[7]
) или по имени свойства с именем в квадратных скобках (matriz["nombre"]
) или с обычным точечным синтаксисом для объектов (matriz.nombre
). Поскольку имя представляет собой текстовую строку, для его составления можно использовать выражение, включая переменные. Чтобы перебрать массив со свойствами, можно использовать цикл с форматом for(propiedad in matriz)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var matriz=[]; matriz[“color”]=“verde”; matriz[“grosor”]=10; matriz[“estado”]=“nuevo”; matriz[0]=25.0; matriz[1]=“uno”; for(propiedad in matriz) { console.log(propiedad+” valor “+matriz[propiedad]); } /* El resultado en la consola será: 0 valor 25 1 valor uno color valor verde grosor valor 10 estado valor nuevo */ |
Для нашей цели интересно рассматривать объект Date
, с помощью которого можно представлять и управлять датой и временем в JavaScript. Объект может быть создан без данных, поэтому он будет принимать текущую дату и время, или его можно создать, указав дату в качестве значения либо в миллисекундах с 1 января 1970 года (например, Время Unix или время POSIX но выражается в миллисекундах, а не в секундах) или указывая отдельные значения года, месяца, дня, часа...
Объект включает в себя полную серию методы для запроса или установки даты и времени:
-
now()
Возвращает текущую дату и время, выраженные в миллисекундах с 1 января 1970 года. -
getTime()
|setTime()
Получает или изменяет соответственно значение времени в миллисекундах с 1 января 1970 года.valueOf()
, который является методом, присутствующим в большинстве объектов, также получается значение соответствующего объекта Date, напримерgetTime()
con Время Unix или время POSIX выражается в мс. -
getMilliseconds()
|setMilliseconds()
Используется для запроса или установки дробной миллисекундной части объекта.Date
на котором оно выполняется. Если проконсультироваться, полученное значение находится в диапазоне от 0 до 999, но можно назначить более крупные значения, которые будут накапливаться в общей дате и времени, поэтому, как и остальные методы get, оно служит для увеличения значения объекта.Date
(или уменьшить его, если используются отрицательные значения). -
getSeconds()
|setSeconds()
Возвращает или изменяет соответственно значение секунд объектаDate
. -
getMinutes()
|setMinutes()
Используется для просмотра или установки минут объекта.Date
. -
getHours()
|setHours()
Позволяет просмотреть или изменить часы (от 0 до 23) объекта.Date
. -
getDay()
Возвращает день недели для даты, выраженный значением от 0 до 6 (с воскресенья по субботу). -
getDate()
|setDate()
Возвращает или изменяет день месяца объектаDate
на котором он применяется. -
getMonth()
|setMonth()
Используется для просмотра или изменения номера месяца объекта.Date
. -
getFullYear()
|setFullYear()
Запрашивает или устанавливает значение года для объекта, содержащего дату и время.
Предыдущие методы Date
включить версию UTC иметь возможность напрямую работать с мировым временем без необходимости выполнять промежуточные вычисления. В этом смысле, например, getHours()
есть версия getUTCHours()
o getMilliseconds()
альтернатива getUTCMilliseconds()
работать альтернативно с официальным (легальным) или всемирным временем. С getTimezoneOffset()
Вы можете узнать разницу между универсальным временем и местным официальным временем.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var dia_semana=[“domingo”,“lunes”,“martes”,“miércoles”,“jueves”,“viernes”,“sábado”]; var nombre_mes=[“enero”,“febrero”,“marzo”,“abril”,“mayo”,“junio”,“julio”,“agosto”,“septiembre”,“octubre”,“noviembre”,“diciembre”]; var digitos_hora; var hoy=new Date(); var texto_hoy=“”; texto_hoy+=“Hoy es “; texto_hoy+=dia_semana[hoy.getDay()]; texto_hoy+=“, “; texto_hoy+=hoy.getDate(); texto_hoy+=” de “; texto_hoy+=nombre_mes[hoy.getMonth()]; texto_hoy+=” de “; texto_hoy+=hoy.getFullYear(); texto_hoy+=” y son las “; digitos_hora=hoy.getHours(); texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora; texto_hoy+=“:”; digitos_hora=hoy.getMinutes(); texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora; texto_hoy+=“:”; digitos_hora=hoy.getSeconds(); texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora; |
Функции JavaScript
Если вы читаете это, вы наверняка умеете программировать. микроконтроллеры en C о ан C + + и знать понятие функции. Хотя основная идея та же, в JavaScript Способ их определения и использования немного отличается. Начнем с того, что уже было сказано: JavaScript Он не использует явным образом типы данных, поэтому вам не нужно указывать его при определении функции.. Следить, Функция не обязательно должна иметь имя, она может быть анонимной.. Их можно связать с переменной для их вызова, но в этом может и не быть необходимости, поскольку иногда бывает полезно вызвать их немедленно, для чего круглые скобки и параметры добавляются после определения функции.
Чтобы определить функцию, добавьте префикс function
, если применимо, напишите имя, аргументы (параметры, передаваемые функции) в круглых скобках, а в фигурных скобках код, который будет выполняться при вызове функции.
1 2 3 4 5 | function doble(numero) { var resultado=numero*2; return resultado; } |
Конечно, в предыдущем примере переменная result была вообще не нужна, но это хороший повод вспомнить область видимости переменной, который работает так, как вы ожидаете: переменная result существует только внутри функции double. В JavaScript также можно использовать let
вместо var
, чтобы ограничить переменную контекстом блока кода (заключенным в фигурные скобки, {
y }
)
Когда мы говорили об объектах в предыдущем разделе, не хватало чего-то фундаментального: свойства были определены, но методы не определены. Как и ожидалось, методы объекта — это функции, они не имеют имени и используются (вызываются) из имени (свойства), присвоенного определением объекта.
1 2 3 4 5 6 7 8 9 10 11 12 13 | var termostato= { temperatura_actual:0.0, temperatura_frio:18.5, temperatura_calor:22.0, consumo:0, ver_temperatura: function() { console.log(“Temperatura actual: “+this.temperatura_actual+” °C”); } } |
В предыдущем примере уже существует метод view_temperature, который отображает значение свойства current_temperature через консоль. Это не очень полезно, но дает более полное представление о том, что такое определение объекта в JavaScript.
Для доступа методов объекта (функций) к его свойствам используйте this
, как в предыдущем примере в строке 11, при использовании свойства current_temperature.
Доступ к объектной модели документа (DOM) с помощью JavaScript
от JavaScript У вас есть доступ к содержимому веб-страницы, на которой она запускается, а также к некоторым аспектам браузера, отображающего эту страницу, но не к системным ресурсам. Структура данных, поддерживающая свойства и методы, доступные из JavaScript часть объекта окна, а именно содержимое объекта (документ HTML) соответствует объекту document
. Хотя иногда это используется для ясности, нет необходимости ставить окно перед методами или свойствами, чтобы ссылаться на них, достаточно, например, использовать document
, нет необходимости писать имя корневого объекта, как в window.document
, пока имеется ссылка на текущее окно.
Наиболее используемая форма найти объект в документе HTML Это через метод getElementById()
, которому в качестве аргумента передается id, который был указан при создании кода HTML. Из того, что было объяснено в предыдущих разделах, легко предположить, что вы также можете получить доступ к компонентам внутри объекта. document
используя точечный синтаксис (document.componente
) или скобки, используя как имя (document["componente"]
), наиболее полезные, такие как числовой индекс, трудные в использовании и непрактичные при доступе к содержимому веб-страницы, составленной вручную.
В лице JavaScript может быть получить элемент, содержащий другой элемент (элемент или родительский узел) консультации по вашей недвижимости parentNode
или ваша собственность parentElement
, разница в том, что родительский элемент (parentElement
) последнего элемента строки DOM Это ноль(null
) и родительский узел (parentNode
) — это сам документ (document
).
к изменить содержимое элемента HTML, например, метка <div>
, Его можно использовать innerHTML
и чтобы изменить его свойства, вы можете назначить ему другой класс с помощью className
или изменить его свойства индивидуально с помощью style
. Просмотр стиля, отображаемого элементом на веб-странице, не обязательно полезен. style
поскольку это может зависеть от нескольких факторов или просто не указано явно. Чтобы проверить стиль элемента, наконец отображаемого на веб-странице, используется метод getComputedStyle..
К элементу документа HTML Ему можно назначить несколько классов, определяющих его внешний вид и поведение, управлять списком классов объекта из JavaScript можно прибегнуть к classList
который предлагает методы add
чтобы добавить новый класс в список, remove
чтобы удалить его, toggle
заменить его или просмотреть содержимое списка классов элемента с помощью item
и contains
, который возвращает класс, занимающий определенную позицию в списке, и значение true
o false
есть ли определенный класс в списке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var contenedor_temperatura=document.getElementById(“temperatura”); // Encontrar el div con id=”temperatura” contenedor_temperatura.innerHTML=“”; // Eliminar el contenido provisionalmente para que no se vean los cambios contenedor.className=“bloque_temperatura”; // Asignar una nueva clase if(temperatura>20) // Si la temperatura es mayor que 20 °C… { contenedor.style.color=“#FF6666”; // …usar el color rojo en lugar del color normal de la clase } contenedor_temperatura.innerHTML=“La temperatura es “+temperatura+” °C”; // Cuando el aspecto esté preparado mostrar el valor if(document[“titulo”].classList.contains(“estilo_titulo”)) // Si el objeto “titulo” tiene la clase “estilo_titulo”… { if(document[“titulo”].classList.contains(“general”)) // …y la clase “general”… { document[“titulo”].classList.remove(“general”); // …quitar la clase “general” } } else // Si el objeto “titulo” no tiene la clase “estilo_titulo”… { document[“titulo”].classList.add(“estilo_titulo”); // …añadir la clase “estilo_titulo” (da igual que tenga o no la clase “general”) } |
В предыдущем примере он расположен с getElementById
объект, которым вы хотите манипулировать (элемент <div>
для его id
), перед изменением внешнего вида содержимое удаляется путем присвоения innerHTML
пустая текстовая строка, ей присваивается новый класс с className
и его стиль изменяется с помощью style
в зависимости от значения содержимого (температуры), изменение цвета, если применимо, через свойство color
. Как только аспект установлен, значение записывается снова с помощью innerHTML
.
Во второй части приведенного выше примера (строки с 9 по 19) осуществляется доступ к элементу кода. HTML используя синтаксис document[]
и собственность id
элемента, чтобы изменить его список классов с помощью метода classList.remove()
и с методомclassList.add()
, на основе результата нескольких запросов, выполняемых в условиях условного выполнения, которые они сравнивают с помощью classList.contains()
.
Когда это будет обратиться к элементу HTML несколько раз по всему коду JavaScript, это немного более эффективно присвоить его переменной или используйте его индекс вместо имени, поскольку в противном случае метод, который вы бы использовали, JavaScript чтобы получить ее каждый раз, потребуется искать ее имя, что отнимет немного больше времени, чем если бы был доступ к переменной.
к добавлять новые объекты в документ HTML, их можно сначала создать с помощью метода createElement
de document
а затем объединить их с остальными элементами в той точке дерева, которая необходима с помощью appendChild
. Чтобы создать объект XML, как предметы SVG который мы используем для построения графика датчиков Интернета вещей, вы можете использовать createElementNS
(НС для пространство имен). Как объяснялось при разговоре о формате SVG, соответствующее ему пространство имен (для текущей версии) равно http://www.w3.org/2000/svg
, который следует передать createElementNS
в качестве аргумента вместе с типом элемента, svg
, в этом случае.
Una Альтернативой innerHTML
добавить текст в качестве содержимого в элемент документа HTML это метод createTextNode()
объект document
. С помощью этой альтернативы вы можете создать новый текст (к которому позже осуществляется доступ, если он присвоен переменной), который включается в дерево объектов с помощью метода appendChild()
. Как Альтернативой appendChild()
, который добавляет новый контент в конец того, что уже существует в узле, к которому он добавляется, вы можете использовать метод insertBefore()
, который добавляет новый объект поверх существующего. Носить insertBefore()
вместо appendChild()
предоставляет метод, который служит, например, для сортировать новые объекты перед существующими когда элемент должен находиться перед другим (как в списке) или перекрывать или перекрывать графическую структуру, в которой есть элементы, расположенные ближе к переднему или заднему плану.
Реагируйте на события с помощью JavaScript
Когда путь использовать веб-страницу в качестве контейнера для графиков датчиков, подключенных к Интернету вещей это было использовано onload
В этикетке <body>
чтобы начать рисовать график. Это свойство, связанное с объектами кода HTML, относится к события JavaScript. Как уже объяснялось, он выполняет функцию после загрузки страницы. Хотя это было связано с кодом HTML чтобы лучше помнить об этом, это можно было бы написать в коде JavaScript в качестве body.onload=dibujar;
siendo dibujar
имя функции, которая должна запускаться при загрузке веб-страницы.
В самых последних версиях JavaScript события могут быть связаны с функциями, используя addEventListener
с форматом objeto.addEventListener(evento,función);
или используя синтаксис objeto.evento=función;
который работает и в старых реализациях. Чтобы отменить связь функции, связанной с событием, у вас есть removeEventListener
который имеет тот же формат, что и addEventListener
.
JavaScript Он способен реагировать на множество событий, которые могут произойти на веб-странице. Например, он может обнаружить щелчок по элементу. HTML con onmousedown
или при нажатии кнопки onclick
, при нажатии клавиши с onkeydown
, управляя полосой прокрутки с помощью onscroll
. Для нашей цели нам достаточно обнаружить загрузку страницы с помощью onload
и его изменение размера с помощью onresize
. Мы свяжем эти события с объектами body
y window
из DOM соответственно. Первое можно назначить в коде HTML, как видно, и второй в коде JavaScript внутри функции, вызванной первой, и с форматом window.onresize=redimensionar;
siendo redimensionar
функция, которая будет вызываться каждый раз, когда окно меняет размер.
Запустить через интервал времени
JavaScript имеет два ресурса для отложенное выполнение: setTimeout
, который выполняет функцию через определенный интервал времени и setInterval
который будет выполнять функцию каждый определенный интервал времени. Оба метода требуют в качестве параметров (1) вызываемую функцию и (2) временной интервал, выраженный в миллисекундах. Чтобы остановить их работу, вы можете присвоить результат, возвращаемый этими функциями, переменным и передать его в качестве аргумента функции. clearTimeout
О.А. clearInterval
когда вы не хотите вызывать их снова (или когда вы не хотите, чтобы они выполнялись в первый раз) setTimeout
o setInterval
соответственно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var cuenta_atras=setTimeout(descansar,1000*60*20); // Recordar que hay que descansar cuando pasen 20 minutos var repeticion=setInterval(consultar_correo,1000*60*5); // Revisar el correo cada 5 minutos function descansar() { alert(“Puedes descansar un rato”); // Esto solamente aparecerá una vez } function consultar_correo() { alert(“Revisa el correo electrónico”); // Esto aparecerá cada cinco minutos } function detener_cuenta_atras() // No utiliza argumentos sino la variable global { clearTimeout(cuenta_atras); // Detener la cuenta atrás para avisar a los 20 minutos } function no_avisar_lectura_correo() // No utiliza argumentos sino la variable global { clearInterval(repeticion); // Dejar de avisar cada 5 minutos } |
В предыдущем примере введен метод alert
который служит для отображения предупреждающего знака. Хотя в прошлом он широко использовался, в настоящее время он практически запрещен в коде. JavaScript из-за того, насколько агрессивно (навязчиво) закрывать веб-страницу диалоговым окном.
В программе, написанной для микроконтроллер небольшой серии (такой, как та, что на тарелке Arduino Uno) обычно используются глобальные переменные, как в предыдущем примере в JavaScript, поскольку код краток и не особо запутан, поскольку во многих случаях функции реализуются ad hoc и поскольку использование глобальных переменных позволяет очень простым и интуитивно понятным образом прогнозировать использование памяти, что критично в системах с небольшим количеством ресурсов. . Вместо, en JavaScript Обычно использование глобальных переменных сводят к минимуму. потому что ему не нужно торопливо использовать память, поскольку он нормально работает на ЦП с ресурсами, намного превосходящими ресурсы MCU, поскольку он, вероятно, будет сосуществовать с большим количеством стороннего кода, с которым он должен работать, не вмешиваясь, и поскольку это открытая система, будущий контекст выполнения невозможно предсказать (программа микроконтроллер small полностью определяет его работу без добавления дополнительного кода после его работы), а также потому, что размеры приложений могут затруднить чтение, если код не инкапсулирует его работу, что делает методы максимально самодостаточными.
Математические операции с объектом JavaScript Math
Математические операции более сложного математического расчета сгруппированы в объекте Math
. Этот объект используется напрямую, нет необходимости создавать его экземпляр для использования методов или свойств (констант), которые он включает.
Math.abs(n)
Абсолютное значение параметра nMath.acos(n)
Арккосинус параметра n (результат в радианах)Math.asin(n)
Арксинус параметра n (результат в радианах)Math.atan(n)
Арктангенс параметра n (результат в радианах)Math.atan2(n,m)
Арктангенс н/м (результат в радианах)Math.ceil(n)
Округлите параметр до ближайшего целого числа вверхMath.cos(α)
Косинус параметра α (α в радианах)Math.E
номер е (≃2.718281828459045)Math.exp(n)
e возведено в параметр n: enMath.floor(n)
Округлите параметр n до ближайшего целого числа внизMath.log(n)
Натуральный логарифм (по основанию e) параметра nMath.LN2
Натуральный логарифм (по основанию e) из 2 (≃0.6931471805599453)Math.LN10
Натуральный логарифм (по основанию e) из 10 (≃2.302585092994046)Math.LOG2E
Логарифм е по основанию 2 (≃1.4426950408889634)Math.LOG10E
Логарифм е по основанию 10 (≃0.4342944819032518)Math.max(a,b,c,…)
Наибольшее значение из списка переданных параметровMath.min(a,b,c,…)
Наименьшее значение из списка переданных параметровMath.PI
Число π (≃3.141592653589793)Math.pow(n,m)
Первый параметр n повышен до второго параметра m: nmMath.random()
(Почти) случайное число от 0.0 до 1.0Math.round(n)
Округлить параметр n до ближайшего целого числаMath.sin(α)
Синус параметра α (α в радианах)Math.sqrt(n)
Квадратный корень из параметра nMath.SQRT1_2
Квадратный корень из 1/2 (≃0.7071067811865476)Math.SQRT2
Квадратный корень из 2 (≃1.4142135623730951)Math.tan(α)
Тангенс параметра α (α в радианах)
Загрузить данные с сервера с помощью AJAX
Метод, используемый для получения информации, хранящейся в IoT, состоит в периодической загрузке данных с сервера и перерисовке графа, с помощью которого они представлены. Для чтения данных с сервера используется технология AJAX (асинхронный JavaScript и XML) через объект XMLHttpRequest
de JavaScript. Построение графика данных осуществляется путем повторного использования объекта. SVG который уже есть в коде HTML и содержит график, координаты которого изменяются, чтобы соответствовать новым загруженным данным.
В примере этого предложения, помимо обновления чертежа, также обновляется текст на веб-странице, который показывает дату и значение последних измеренных данных для каждого графика.
На стороне сервера имеется база данных, содержащая информацию что датчики, подключенные к Интернету вещей, отслеживают. Эта база данных считывается запросом объекта XMLHttpRequest
отвечая информацией, закодированной в Формат JSON, хотя название используемого метода предполагает связь с форматом XML.
В первом уроке Polaridad.es по Хранение данных Интернета вещей Вы можете увидеть пример инфраструктуры для управления со стороны сервера информацией, предоставляемой устройствами, подключенными к Интернету вещей. В этой серии статей в качестве ресурса используется сервер. апаш из которого вы можете использовать язык программирования PHP для доступа к базе данных MySQL o MariaDB. На серверах, используемых для поддержки IoT, очень часто встречаются базы данных. MongoDB (NoSQL) и язык программирования JavaScript на Node.js как программная инфраструктура.
Следующая функция отвечает за запрос с сервера последних данных одного из датчиков. При вызове функции объект используется в качестве аргумента. JavaScript который поддерживает рисуемые данные. Если один и тот же график представляет несколько значений, например, для визуального поиска корреляции, можно отправить запрос серверу на возврат нескольких значений одновременно, что является более оптимальным методом из-за особенностей работы сервера. HTTP-протокол.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | function consultar_ultimo_valor_sensores(objeto_grafico) { var consulta=‘zona=”+objeto_grafico.sufijo_nombre; var pagina=“ultimo_valor_sensor.php”; var resultado; var ajax; if(window.XMLHttpRequest) { ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest); } else // Versiones antiguas de MS Internet Explorer { ajax=new ActiveXObject(“Microsoft.XMLHTTP”); } ajax.onreadystatechange= function() { if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”) { resultado=JSON.parse(ajax.responseText); if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1]) { // Normalmente se gestionará la respuesta utilizando el objeto redibujar_grafico(objeto_grafico,resultado); // Si los datos son sencillos y tienen una estructura clara puede ser más práctico usarla directamente // redibujar_grafico(objeto_grafico,[resultado.fecha,resultado.temperatura]); } } } ajax.open(“POST”,pagina); ajax.setRequestHeader(“Method”,“POST “+pagina+” HTTP/1.1″); ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”); ajax.setRequestHeader(“Content-length”,consulta.length); ajax.setRequestHeader(“Connection”,“close”); ajax.send(consulta); } |
В третьей строке предыдущего примера подготавливается запрос, который будет сделан к серверу, в котором будет передан аргумент «зона», значением которого будет название или код контролируемого места, поскольку информация о В одной базе данных области могут сосуществовать разные датчики (например, термометры, измеряющие температуру в разных помещениях). Ожидается, что параметр, переданный предыдущей функции, объект с данными диаграммы, будет включать свойство с названием комнаты («name_suffix»).
Между строками 7 и 14 предыдущего кода объект XMLHttpRequest
который хранится в переменной «ajax». Прежде чем выбрать способ создания объекта, вы ищете window
если XMLHttpRequest
был недоступен (то, что происходило в старых версиях проводника Microsoft, и хотя он сильно отстает, он служит примером альтернатив для создания объекта с использованием (более собственного) синтаксиса. Object.create
o new
, аналогично другим объектно-ориентированным языкам.
Чтобы иметь возможность немедленно управлять ответом, код, который его обрабатывает, подготавливается в строках с 15 по 26 перед отправкой запроса на сервер.
Форма сделать запрос HTTP на сервер состоит из открыть соединение con open
указание типа и страницы (необязательно имя пользователя и пароль), подготовить заголовки протокола с setRequestHeader
y отправить запрос con send
. Заголовок HTTP Content-length
вам нужно будет знать длину запроса (количество символов), которая рассчитывается с помощью length
.
Когда запрос AJAX готово, функция, связанная с событием, выполняется onreadystatechange
. Вместо назначения функции в предыдущем примере на лету определяется анонимная функция, которая будет управлять приемом данных, поступающих с сервера. Прежде всего в строке 18 проверяется, что статус запроса «завершен», что соответствует значению 4
собственности readyState
, что статус «ОК» HTTP-протокол (код 200
), которые можно получить из имущества status
и что полученные данные Формат JSON, консультации по объекту недвижимости responseType
.
После проверки того, что статус ответа соответствует ожиданиям, в строке 20 предыдущего примера создает объект с результатом, преобразуя текст JSON. В ответе предусмотрен возврат даты, это позволяет нам увидеть, был ли результат, который отправляет сервер, уже ранее представлен на графике, что проверяется в строке 21. Если данные новые, в строке 23 Функция, которая отвечает за перерисовку графика с учетом новой информации.
Идея предложения этого метода чтения заключается в том, что данные будут обновляться очень часто. Если представленная информация соответствует долгосрочному периоду (например, температуре дня или недели), можно реализовать первоначальный запрос, который собирает все доступные данные, а затем другой, аналогичный показанному в примере, который обновляет их в корреспондент периода.
Генерация случайных данных для тестирования
Когда вся серверная и клиентская инфраструктура будет готова, функция, подобная той, что была в предыдущем разделе, будет отвечать за чтение данных и построение на их основе графика, но На этапе тестирования может оказаться более практичным использовать случайные числа в контролируемом диапазоне. чтобы убедиться, что написанный код верен. Следующая функция может служить примером получения данных при создании окончательного приложения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function consultar_ultimo_valor_sensores ( objeto_grafico,// Objeto dentro del elemento SVG que representa el trazado valor_maximo, // Valor máximo valor_minimo, // Valor mínimo margen_valor // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo ) { // La forma más genérica es usar objetos /* var nuevo_valor_inventado= { fecha:Math.round(Date.now()+Math.random()*1000), temperatura:Math.random()*Math.abs(valor_maximo-valor_minimo)+valor_minimo }; */ // En este caso concreto, como son pocos datos y con una estructura concreta es más práctico usar un vector var nuevo_valor_inventado= [ Math.round(Date.now()+Math.random()*1000), Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo ]; redibujar_grafico(objeto_grafico,nuevo_valor_inventado); } |
Вместо чтения информации из базы данных, приведенный выше пример генерирует ее случайным образом и передает в функцию, отвечающую за рисование графика. Придуманные данные представляют собой вектор, образованный датой, выраженной в виде значения в миллисекундах, моментом записи информации датчика и контролируемыми данными, которые находятся между максимальным значением и минимальным значением.
В этом примере при генерации даты она может быть задержана до одной секунды (1000 миллисекунд) относительно даты на момент изобретения. Как Math.random()
генерирует число от 0.0 до 1.0, умножая его на 1000, получается число от 0 до 1000, которое затем преобразуется в целое число. Таким же образом значение получается путем умножения случайного числа на диапазон (максимум минус минимум) и сложения минимума.
Нарисуйте график датчиков Интернета вещей с помощью графика SVG.
Поскольку мы увидели, как мы можем получить значения, которые мы хотим представить (температуру в примере), и их временное местоположение, которое можно выразить вместе в виде координат, в примере ниже показана функция для рисования пути. который соединяет эти точки и, возможно, цветную область, ограниченную этой линией вверху. Результат будет похож на следующее изображение.
Горизонтальная ось (X) графика представляет время, а вертикальная ось (Y) — значения, которые отслеживают датчики, подключенные к Интернету вещей. Горизонтальный интервал составляет несколько секунд, поскольку в этом предложении график обновляется очень часто (например, каждую секунду), чтобы предоставлять информацию о состоянии датчиков практически в реальном времени.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | function actualizar_grafico ( grafico, // Objeto SVG con el que se dibuja la gráfico coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos) valor_maximo, // Valor máximo aceptable antes de emitir una alarma valor_minimo, // Valor mínimo aceptable antes de emitir una alarma margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false) parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0) parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0) ) { var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false) var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0) var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0) var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos) var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice) var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido) if(cerrado) // Si se dibuja un path (trazado) cerrado… { coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja } while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico… { posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG) coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo… { coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo contador_valor—; // Pasar al siguiente valor } else // Si se ha rebasado el margen izquierdo… { sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas) } } if(cerrado) // Si se dibuja un trazado (path) cerrado… { coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG } grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado } |
В предыдущем коде есть два интересных аспекта: во-первых, расчет, позволяющий адаптировать диапазон представленных значений и, во-вторых, строительство недвижимости d
где указаны координаты точек на макете (path
).
Чтобы адаптировать диапазон представленных значений, они перемещаются от минимума и масштабируются так, чтобы видимая величина соответствовала размеру графика.. В случае времени смещение получается путем вычитания диапазона, который вы хотите отобразить, из самого длительного времени (даты и времени, наиболее близких к текущему) (20 секунд в примере). Смещение значений температуры соответствует нижнему диапазону (один градус) минус самое низкое значение, так что данные, показанные ниже, наиболее похожи на минимально допустимое значение, но оставляют запас, который позволяет нам оценить, что является допустимым.
Коэффициент, который умножает значения времени для получения горизонтальных координат графика, получается путем деления общей ширины графика (100 единиц в примере) на представленный временной диапазон (20 секунд в примере). Чтобы получить коэффициент со скалярными значениями температуры, необходимо помнить, что представленный диапазон варьируется от запаса ниже минимального значения до запаса выше максимального значения, на один градус в обоих случаях. Таким образом, коэффициент вертикального масштаба получается в результате деления высоты графика (в примере 100 единиц) на максимальное значение минус минимальное плюс верхнее и нижнее поля. Поскольку эти значения могли полностью развиваться при отрицательных температурах, мы используем Math.abs()
использовать абсолютное значение разницы.
Недвижимость d
объект path
Он создается путем объединения координат точек в тексте.. Каждой паре координат предшествует код SVG L
, который рисует линию от текущей позиции до абсолютного значения, указанного в координатах. Значения X и Y разделяются запятыми и каждая операция SVG отделяется пробелом от следующего.
Для запуска верстки используйте код M
(перейти к абсолютной координате). В случае закрытого и заполненного графика вы начинаете с нижнего правого угла, в случае открытого графика, на котором рисуется профиль данных, вы начинаете с последнего представленного значения (самого последнего). Для завершения закрытого макета используется код Z
добавление в качестве последней точки точки, которая имеет то же значение координаты X, что и последняя точка линии, а в качестве координаты Y — наименьшее представленное значение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | function dibujar_grafico() { var tiempo_mostrado=20000; // Se representan 20 segundos (20000 milisegundos) var valor_maximo=10; // 10 grados sobre cero var valor_minimo=–5; // Cinco grados bajo cero var fecha_hora=Date.now(); // Hora actual var matriz_de_coordenadas_de_prueba=[]; // Preparar el vector de coordenadas for(var contador=0;contador<20;contador++) { fecha_hora+=500+1500*Math.random(); // Añadir medio segundo a la hora anterior y entre 0 y segundo y medio aleatoriamente matriz_de_coordenadas_de_prueba[contador]=[]; // Preparar el siguiente punto del vector de coordenadas matriz_de_coordenadas_de_prueba[contador][0]=fecha_hora; // En la coordenada horizontal, situar la hora matriz_de_coordenadas_de_prueba[contador][1]=Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo; // En la coordenada vertical situar el valor del sensor } actualizar_grafico ( document.getElementById(“relleno_temperatura”), // Trazado para el relleno definido en el código HTML matriz_de_coordenadas_de_prueba, tiempo_mostrado, valor_maximo, // Diez grados de valor máximo valor_minimo, // Cinco grados bajo cero como valor mínimo 1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente true // Cerrar el trazado para representar el área rellena ); actualizar_grafico ( document.getElementById(“linea_temperatura”), // Trazado para el relleno definido en el código HTML matriz_de_coordenadas_de_prueba, tiempo_mostrado, valor_maximo, // Diez grados de valor máximo valor_minimo, // Cinco grados bajo cero como valor mínimo 1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente false // No cerrar el trazado para representar la linea que une los puntos que representan las temperaturas ); } |
В этом примере функция dibujar_grafico()
, который является вызовом при загрузке страницы, получает начальные значения для проверки (а не последнее значение в реальном времени) и подготавливает диапазон, в котором данные будут отображаться: 20 секунд (20000 мс) по горизонтали и 15°C по горизонтали. по вертикали от -5°C до +10°C с верхним и нижним полем в один градус. Сделайте два звонка на actualizar_grafico()
, в первом проходе true
в качестве аргумента, который указывает, что диаграмма должна быть закрыта, чтобы представить заполненную область, и при втором вызове он передается false
провести линию. В каждом случае объект path
модифицированный — тот, который имеет соответствующий вид, с заливкой и без рамки в первом случае и с определенной толщиной линии и без заливки во втором.
Функция actualizar_grafico()
работа над объектом SVG который использует следующий код в качестве контейнера HTML. Объект SVG содержит два пути: один для рисования линии, а другой для рисования заполненной области. При загрузке веб-страницы из элемента <body>
предыдущая функция вызывается автоматически, dibujar_grafico()
благодаря событию JavaScript onload
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!DOCTYPE html> <html lang=“es”> <head> <meta charset=“utf-8”> <title>Temperatura</title> <script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script> </head> <body onload=“dibujar_grafico();” style=“margin:0;”> <!– Cuerpo del documento HTML. Al cargar el contenido llama a la función JavaScript dibujar_grafico() –> <div id=“temperatura”> <div id=“bloque_temperatura” style=“width:820px;height:150px”> <svg id=“grafico_temperatura” width=“100%” height=“100%” viewBox=“0 0 100 100” preserveAspectRatio=“none”> <path id=“relleno_temperatura” d=“” style=“fill:#A8C3EA;stroke:none;” vector-effect=“non-scaling-stroke” /> <path id=“linea_temperatura” d=“” style=“fill:none;stroke:#205587;stroke-width:4;stroke-opacity:1;” vector-effect=“non-scaling-stroke” /> </svg> </div> </div> </body> </html> |
В строке 10 кода HTML выше в стиле установлена ширина (как пример) 820 px и высота 150 px (то, что в финальной версии желательно будет сделать с классом и документом CSS). Кажется странным, что строки 13 и 14 определяют размер объекта. SVG например, 100% ширины и высоты (которые лучше всего соответствуют размерам окна, 100×100). Как уже упоминалось, причина этого заключается в том, чтобы всегда работать с известными размерами и подгонять к ним представленные значения. Другой альтернативой было бы каждый раз рассчитывать пространство графика, а затем корректировать значения или принудительно устанавливать фиксированные размеры графика, которых должен будет придерживаться документ.
Выбрав график, размеры которого меняются в соответствии с кодом HTML, необходимо включить свойство vector-effect
с мужеством non-scaling-stroke
чтобы предотвратить деформацию толщины линий, когда график не поддерживает выбранные пропорции 1:1 на веб-странице, на которой он отображается, как это происходит в предыдущем предложении.
Чтобы «обрезать» график и показать только выбранную вами область, используйте viewBox
. В данном случае мы решили просмотреть часть графика, которая начинается с 0,0 (верхний левый угол) и имеет размеры 100x100 вниз и вправо. Часть чертежа, расположенная в координатах с отрицательными значениями или больше 100, не будет отображаться на веб-странице, даже если они существуют в объекте. SVG
Добавьте новые элементы в рисунок SVG.
В предыдущем примере функция actualizar_grafico()
использовать макет SVG на который меняется право собственности d
, что выражает цепочку координат. Альтернативой было бы создавать весь объект каждый раз, когда он перерисовывается. Преимущество первого варианта заключается в том, что внешний вид графики (например, толщина или цвет) определяется в коде. HTMLограничением является то, что объекты должны быть предварительно созданы.
Чтобы создать объекты SVG, используйте createElementNS()
, что позволяет включить пространство имен. В приведенном ниже примере создается новый текстовый объект (text
) и связан с элементом SVG который уже существует в коде HTML веб-сайта. После создания нового элемента его свойствам присваиваются значения setAttribute()
и добавляется в SVG con appendChild()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function rotular ( objeto_grafico, texto, inicio=[0,0], altura=10.0, tipo_letra=“SircuitoRegularMedium”, color_texto=“#000000”, color_fondo=“#FFFFFF” ) { nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,“text”); nuevo_objeto_svg.setAttribute(“x”,inicio[0]); nuevo_objeto_svg.setAttribute(“y”,inicio[1]); nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra); nuevo_objeto_svg.setAttribute(“font-size”,altura); nuevo_objeto_svg.setAttribute(“fill”,color_texto); nuevo_objeto_svg.textContent=texto; objeto_grafico.appendChild(nuevo_objeto_svg); } //rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]); |
Изменить пропорции элементов рисунка
Если вы пробовали разметку с помощью функции из примера в предыдущем разделе, вы заметили, что текст деформируется при изменении пропорции объекта на веб-странице (width
y height
Кода HTML) не равна представленной площади (viewBox
). Чтобы адаптировать пропорцию, необходимо знать размеры объекта. SVG для чего вы можете просмотреть стиль объекта или контейнера HTML, если объект SVG передать это имущество. Передача права собственности transform
к объектам SVG которые зависят от пропорции, деформацию можно исправить, применив операцию масштабирования scale()
в котором коэффициент в X отличается от коэффициента в Y.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function rotular ( objeto_grafico, texto, inicio=[0,0], altura=10.0, proporcion=1.0, tipo_letra=“SircuitoRegularMedium”, color_texto=“#000000”, color_fondo=“#FFFFFF” ) { var escala_horizontal=parseFloat(getComputedStyle(objeto_grafico).height)/parseFloat(getComputedStyle(objeto_grafico).width); nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,‘text’); nuevo_objeto_svg.setAttribute(“transform”,“scale(“+escala_horizontal+“,1.0)”); // scale permite cambiar la escala en X e Y //nuevo_objeto_svg.setAttribute(“transform”,”scaleX(“+escala_horizontal+”)”); // Como se sabe que sólo cambia la escala en X, se puede usar scaleX nuevo_objeto_svg.setAttribute(“x”,inicio[0]); nuevo_objeto_svg.setAttribute(“y”,inicio[1]); nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra); nuevo_objeto_svg.setAttribute(“font-size”,altura); nuevo_objeto_svg.setAttribute(“fill”,color_texto); nuevo_objeto_svg.textContent=texto; objeto_grafico.appendChild(nuevo_objeto_svg); } //rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]); |
SVG позволяет группировать несколько объектов, образуя новый составной элемент, который также поддерживает свойства, как простые объекты. Чтобы применить одно и то же преобразование к серии объектов сразу, а не к каждому объекту в отдельности, вы можете сгруппировать их по этому ресурсу и применить одно свойство. transform
всем им.
Как объяснялось, когда речь шла о Формат SVG, элементы группы заключаются в метки <g>
y </g>
. Чтобы добавить из JavaScript элементы в группу SVG используется, как видно из предыдущего примера, appendChild()
как только новый объект определен.
Чтобы установить начало координат при применении преобразований, свойство можно использовать на объектах. SVG transform-origin
, значением которого являются координаты X и Y точки, с которой начинается преобразование. Если значение начала преобразования явно не указано (в веб-браузере), используется центр координат. К сожалению, на момент написания указание поведения преобразований с использованием источника, отличного от источника по умолчанию, не является одинаковым для всех браузеров, и его следует использовать с осторожностью.
Наряду с преобразованием масштаба с scale
Есть и другие, такие как вращение с rotation
и движение с translate
, которые предлагают альтернатива графическому представлению: вместо получения новых координат вы можете представить их в их собственном пространстве и преобразовать график в соответствии с форматом, в котором вы хотите их представить.
Добавьте ссылки на диаграмму
Теперь, когда основная часть графика решена путем нанесения значений с профилем и заполненной областью, его можно дополнить ссылками, помогающими его считыванию. В качестве примера начнем с рисования горизонтальных ссылок (линий), обозначающих максимальное и минимальное допустимые значения, а также желаемое значение. Как объяснялось, вы можете добавить объекты в SVG прямо от JavaScript или включить их вручную в код HTML и измените их позже с помощью JavaScript.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS) var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia var VALOR_DESEADO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema var MARGEN_VALOR=1.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo function inicializar_grafico() { CONTENEDOR_SVG=document.getElementById(“contenedor_svg”); crear_referencia_horizontal_svg(VALOR_MAXIMO,“#FF0000”); crear_referencia_horizontal_svg(VALOR_DESEADO,“#00FF00”); crear_referencia_horizontal_svg(VALOR_MINIMO,“#0000FF”); } function crear_referencia_horizontal_svg ( altura=0.0, color=“#000000”, grosor=0.5, opacidad=1.0 ) { var altura_corregida=ALTO_CAJA–(altura+MARGEN_VALOR–VALOR_MINIMO)*ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2); var referencia_horizontal=document.createElementNS(NS,‘line’); referencia_horizontal.setAttribute(“x1”,0.0); referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA); referencia_horizontal.setAttribute(“y1”,altura_corregida); referencia_horizontal.setAttribute(“y2”,altura_corregida); referencia_horizontal.style.stroke=color; referencia_horizontal.style.strokeWidth=grosor; referencia_horizontal.style.strokeOpacity=opacidad; CONTENEDOR_SVG.appendChild(referencia_horizontal); } //inicializar_grafico(); |
Кажется логичным пометить эти горизонтальные ссылки текстом, поясняющим значение, которое они представляют. Чтобы выделить текст, вы можете использовать прямоугольники, которые будут выделяться на фоне и графике. Поскольку тексты придется масштабировать, чтобы компенсировать деформацию, их все можно сгруппировать в объект, к которому будет применен масштаб; Основное преимущество такого способа — возможность изменить их за одну операцию, если размер контейнера графа (окна браузера) изменяется и изменяется та пропорция, которую корректирует масштаб.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | // Este código de ejemplo usa constantes para hacerlo más legible a desarrolladores de aplicaciones para microcontroladores de series pequeñas. La opción más recomendable, y más propia del estilo JavaScript, es crear un objeto cuyas propiedades serían las constantes y que incluiría los métodos (aquí funciones) que generan o modifican el gráfico o en este caso las referencias var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS) var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones var TIEMPO_REPRESENTADO=30000; // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado) var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia var VALOR_OPTIMO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema var MARGEN_VALOR=2.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo var desplazamiento_valor=desplazamiento(Date.now()); var escala_valor=escala(); var TIPOGRAFIA=“SircuitoRegularMedium”; // Tipografía con la que se rotula todo el gráfico (se usa una constante buscando la uniformidad, pero se puede rotular usando diferentes tipos de letra si es necesario) var ALTURA_TEXTO=10.0; // Altura de los textos en valor absoluto (píxeles) var COLOR_MINIMO=“#621D87”; // Color de la referencia que indica el valor mínimo var COLOR_OPTIMO=“#1D8762”; // Color de la referencia que indica el valor máximo var COLOR_MAXIMO=“#871E35”; // Color de la referencia que indica el valor óptimo var COLOR_TIPOGRAFIA=“#A8C3EA”; // Color del tipo de letra con que se rotulan las referencias var GROSOR_REFERENCIA=0.5; // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles) var OPACIDAD_REFERENCIA=1.0; // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella var RELLENO_FONDO_REFERENCIA=4.0; // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico) var MARGEN_FONDO_REFERENCIA=10.0; // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico) var ANCHO_FONDO_REFERENCIA=34.0; // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto function proporcion_grafico() { return parseFloat(getComputedStyle(CONTENEDOR_SVG).height)/parseFloat(getComputedStyle(CONTENEDOR_SVG).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño } function medida_grafico() { var proporcion=[]; // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web proporcion[0]=ANCHO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).width); proporcion[1]=ALTO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).height); return proporcion; } function desplazamiento(valor_mayor) { var desplazamiento_valor=[]; desplazamiento_valor[0]=valor_mayor–TIEMPO_REPRESENTADO; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado desplazamiento_valor[1]=MARGEN_VALOR–VALOR_MINIMO; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido) return desplazamiento_valor; } function escala() { var escala_valor=[]; escala_valor[0]=ANCHO_CAJA/TIEMPO_REPRESENTADO; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X escala_valor[1]=ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y return escala_valor; } function crear_referencia_horizontal_svg ( posicion=0.0, color_dibujo=“#000000”, grosor_linea=GROSOR_REFERENCIA, opacidad_linea=OPACIDAD_REFERENCIA, color_texto=COLOR_TIPOGRAFIA, altura_texto=ALTURA_TEXTO ) { var proporcion_horizontal=proporcion_grafico(); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño var coeficiente_medida=medida_grafico(); var posicion_corregida=ALTO_CAJA–(posicion+desplazamiento_valor[1])*escala_valor[1]; var referencia_horizontal=document.createElementNS(NS,‘line’); // El orden en el que se crean los objetos determina qué tapa (lo último) y que es tapado (lo primero) var fondo_referencia=document.createElementNS(NS,‘rect’); // El rectángulo (opaco) se creará después de la línea (que puede ser un poco transparente) para definir con claridad el fondo del texto var texto_referencia=document.createElementNS(NS,‘text’); // El texto se creará en último lugar para que quede sobre los otros objetos referencia_horizontal.setAttribute(“x1”,0.0); referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA); referencia_horizontal.setAttribute(“y1”,posicion_corregida); referencia_horizontal.setAttribute(“y2”,posicion_corregida); referencia_horizontal.style.stroke=color_dibujo; referencia_horizontal.style.strokeWidth=grosor_linea*coeficiente_medida[1]; referencia_horizontal.style.strokeOpacity=opacidad_linea; CONTENEDOR_SVG.appendChild(referencia_horizontal); // Añadir la línea de referencia lo más abajo fondo_referencia.setAttribute(“x”,MARGEN_FONDO_REFERENCIA*coeficiente_medida[0]); fondo_referencia.setAttribute(“y”,posicion_corregida–(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]/2.0); fondo_referencia.setAttribute(“width”,ANCHO_FONDO_REFERENCIA*coeficiente_medida[0]); fondo_referencia.setAttribute(“height”,(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]); fondo_referencia.style.fill=color_dibujo; fondo_referencia.style.fillOpacity=1.0; fondo_referencia.style.strokeWidth=0; CONTENEDOR_SVG.appendChild(fondo_referencia); texto_referencia.setAttribute(“x”,(MARGEN_FONDO_REFERENCIA+RELLENO_FONDO_REFERENCIA)*coeficiente_medida[0]/proporcion_horizontal); texto_referencia.setAttribute(“y”,posicion_corregida+ALTURA_TEXTO/2.0*coeficiente_medida[1]); texto_referencia.setAttribute(“font-family”,TIPOGRAFIA); texto_referencia.setAttribute(“font-size”,ALTURA_TEXTO*coeficiente_medida[1]); texto_referencia.setAttribute(“fill”,COLOR_TIPOGRAFIA); texto_referencia.setAttribute(“transform”,“scale(“+proporcion_horizontal+“,1.0)”); texto_referencia.textContent=(posicion>=0?“+”:“”)+posicion; CONTENEDOR_SVG.appendChild(texto_referencia); } function inicializar_grafico() { CONTENEDOR_SVG=document.getElementById(“contenedor_svg”); crear_referencia_horizontal_svg(VALOR_MAXIMO,COLOR_MAXIMO); crear_referencia_horizontal_svg(VALOR_OPTIMO,COLOR_OPTIMO); crear_referencia_horizontal_svg(VALOR_MINIMO,COLOR_MINIMO); } //inicializar_grafico(); |
В приведенном выше примере кода есть несколько интересных аспектов. Прежде всего, отметим, что константы (глобальные переменные) были использованы для того, чтобы сделать пример более читабельным для пользователей, имеющих опыт программирования. микроконтроллеры en C о ан C + +. Как будет видно позже, оптимальный способ запрограммировать его в JavaScript Было бы использование объектов, которые содержали бы эти значения и методы, которые бы управляли ссылками в этом примере или графом, в общем, в производственной системе.
С другой стороны, в целях развития более общего кода были разработаны отдельные функции, которые вычисляют различные коэффициенты, которые корректируют пропорции графика для корректировки текста. proporcion_grafico()
, масштаб значений в зависимости от их диапазона escala()
и поправочный коэффициент для измерений, которые известны в абсолютном значении, например, измерений в эталонных образцах. medida_grafico()
.
Чтение этого кода должно помочь прояснить контекст, в котором работает такое приложение, которое рисует графику в реальном времени и должно быть гибким, чтобы быть представленным в различных графических контекстах (по крайней мере, различных размеров и пропорций). Прежде всего, объекты должны быть сгенерированы. SVG, либо «вручную» в коде HTMLлибо через код JavaScript и в любом случае ссылки на эти объекты впоследствии должны быть получены для манипулирования ими из JavaScript так что можно рисовать новые графики и адаптировать представление уже нарисованного графа к изменению среды, в которой он представлен.
Еще одна ссылка, которая может помочь легко интерпретировать график, — это точки, представляющие определенные значения (узлы линии). В этом примере, в котором мы представляем одну величину, выбор символа не имеет решающего значения, но если для поиска корреляции накладываются несколько разных значений, интересно различать их, помимо использования других ресурсов, таких как цвет. , рисуя разные символы. Графика, используемая для линейного узла, должна быть изменена по размеру и пропорциям, как это происходит, например, с текстами, чтобы ее размеры были абсолютными и чтобы ее пропорции сохранялись, даже если изменяются пропорции содержащегося в ней блока.
В предыдущем примере мы уже видели, как рассчитать различные коэффициенты для изменения масштаба и коррекции пропорций рисунка; Что касается того, как реализовать управление символами узлов или вершин графа, возможным решением может быть хранение объектов SVG в вектор и изменять его положение при обновлении графика путем считывания нового значения или при его перерисовке путем изменения размера контейнера. В первом случае пришлось бы изменить его положение, а во втором – его соотношение с имуществом. transform
и ценность scale
. Следующий код представляет собой модификацию функции actualizar_grafico()
включить перестановку символов вершин графа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | function actualizar_grafico_puntos ( grafico, // Objeto SVG con el que se dibuja la gráfico coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo puntos, // Matriz con los objetos SVG que se representan en los nodos de la línea (los valores reales) tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos) valor_maximo, // Valor máximo aceptable antes de emitir una alarma valor_minimo, // Valor mínimo aceptable antes de emitir una alarma margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false) parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0) parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0) ) { var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false) var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0) var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0) var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos) var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice) var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido) if(cerrado) // Si se dibuja un path (trazado) cerrado… { coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja } while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico… { posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG) punto[contador_valor].setAttribute(“cx”,posicion[0]); punto[contador_valor].setAttribute(“cy”,posicion[1]); coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo… { coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo } else // Si se ha rebasado el margen izquierdo… { sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas) } contador_valor—; // Pasar al siguiente valor } if(cerrado) // Si se dibuja un trazado (path) cerrado… { coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG } grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado for(;contador_valor>=0;contador_valor—) { punto[contador_valor].setAttribute(“cx”,–10000); punto[contador_valor].setAttribute(“cy”,0); } } |
Изменения, внесенные в функцию actualizar_grafico()
чтобы получить новую функцию actualizar_grafico_puntos()
Это те, которые выделены в коде предыдущего примера. Сначала в строке 5 берём вектор объектов SVG в качестве параметра. Этот вектор будет содержать символы, которые необходимо переместить в новых узлах графа.
В строках 39 и 40 заданы новые координаты центра, cx
y cy
, к тем значениям, которые представляются. Если символ основан не на центре, возможно, потребуется добавить смещение в cx
на половину ширины и в cy
половины высоты, чтобы переместить их точно на узел графа.
В строках с 57 по 61 точки, соответствующие координатам, которые не нарисованы, поскольку они обрезаны левым краем, перемещаются за пределы графика. Координата cy
до нуля и что cx
любому отрицательному числу (большему, чем сама точка), чтобы оно не отображалось при обрезке, как и левая часть графика, окном SVG.
Управляйте диаграммой из объекта с помощью JavaScript
Все операции, которые были объяснены до сих пор, могут быть интегрированы в объект для управления графиком в стиле, более типичном для новых версий JavaScript. Этот альтернативный вариант реализации имеет дополнительное преимущество, заключающееся в упрощении включения нескольких графиков с разными значениями на одну и ту же веб-страницу.
Прежде чем обсуждать реализацию, давайте рассмотрим наиболее распространенные способы создания объектов с помощью JavaScript и некоторые особенности функций, которые влияют на предложение по рисованию сенсорной графики IoT.
Уже объяснялось, что новый способ создания объектов в JavaScript (доступно начиная с версии 5 ECMAScript) состоит из использования Object.create
, который надо привыкнуть использовать вместо "классического" new
, который, конечно, по-прежнему работает корректно, хотя его цель — скорее симулировать стиль языков с объектами на основе классов (JavaScript основывает создание объектов на прототипах), чем работающая альтернатива.
1 2 3 4 5 6 7 8 9 10 | <!DOCTYPE html> <html lang=“es”> <head> <meta charset=“utf-8”> <title>Crear objetos con new o con Objetct.create</title> <script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/crear_objetos.js”></script> </head> <body onload=“empezar();”> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | function Objeto_clasico(primero,segundo) { this.primero=primero||1; this.segundo=segundo||2; this.hacer_algo=function() { console.log(“El objeto clásico le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”); } } var Objeto_ES5= { primero:10, segundo:20, hacer_algo: function() { console.log(“El objeto ES5 le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”); } } var objeto_clasico=new Objeto_clasico(); var objeto_ES5=Object.create(Objeto_ES5); function empezar() { console.log(“Objeto clásico (“+objeto_clasico.primero+“,”+objeto_clasico.segundo+“)”); console.log(“Objeto ES5 (“+objeto_ES5.primero+“,”+objeto_ES5.segundo+“)”); objeto_clasico.hacer_algo(); objeto_ES5.hacer_algo(); } |
Предыдущий код позволяет запомнить различия между созданием объектов с помощью Object.create
или new
. Это также подчеркивает, что, хотя функция, с помощью которой создается объект, new
может находиться где угодно в коде, объект должен уже существовать, прежде чем его можно будет создать с помощью Object.create
(Объект ES5_Object не является функцией).
В строках 3 и 4, чтобы установить значение по умолчанию для свойств в функции, которая создает объект с помощью new
, каждому свойству присваивается значение соответствующего аргумента или (||
), если аргументы не были переданы, то есть если они не определены (undefined
), так как это обстоятельство оценивается как false
, назначается значение по умолчанию.
Контекст, в котором выполняется функция JavaScript поднимает две проблемы, которые важно иметь в виду и которые также могут сбивать с толку при использовании этого языка программирования после работы с другими, например: C o C + +, в нашем случае. Контекст включает в себя переменные, определенные в области видимости функции (и глобальные), что, кстати, порождает интересную концепцию «замыканий», которая устанавливает целый стиль программирования в JavaScript. Тем не менее, можно было ожидать, что this
, который ссылается на объект при использовании в коде, который его определяет, контекст выполнения, в котором он был определен, сохраняется, но тот, который он использует, является контекстом, из которого вызывается функция. В большинстве случаев такое поведение прозрачно, но есть два обстоятельства, при которых оно может сбить с толку: функция, определенная внутри другой функции, и метод, вызываемый из события объекта. window
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | var primero=“Primero global”; var segundo=“Segundo global”; var Contexto= { primero:“Primero en contexto”, segundo:“Segundo en contexto”, probar: function() { console.log(“Primero en contexto: “+this.primero); console.log(“Segundo en contexto: “+this.segundo); function probar_dentro() { console.log(“Primero dentro: “+this.primero); console.log(“Segundo dentro: “+this.segundo); } probar_dentro(); } } var probador=Object.create(Contexto); probador.probar(); /* Primero en contexto: Primero en contexto Segundo en contexto: Segundo en contexto Primero dentro: Primero global Segundo dentro: Segundo global */ |
При выполнении предыдущего кода закомментированный текст в конце отображается в консоли. Две отмеченные линии отражают поведение, которое может сбить с толку: контекст выполнения функции. probar_dentro()
не probar()
как и следовало ожидать, но window
, который показывает глобальные переменные, а не свойства с тем же именем. Если вы не хотите такого поведения, просто создайте переменную в функции самого высокого уровня и присвойте ее this
, как в следующем коде.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var primero=“Primero global”; var segundo=“Segundo global”; var Contexto= { primero:“Primero en contexto”, segundo:“Segundo en contexto”, probar: function() { var esto=this; console.log(“Primero en contexto: “+esto.primero); console.log(“Segundo en contexto: “+esto.segundo); function probar_dentro() { console.log(“Primero dentro: “+esto.primero); console.log(“Segundo dentro: “+esto.segundo); } probar_dentro(); } } var probador=Object.create(Contexto); probador.probar(); /* Primero en contexto: Primero en contexto Segundo en contexto: Segundo en contexto Primero dentro: Primero en contexto Segundo dentro: Segundo en contexto */ |
Для управления контекстом выполнения при вызове метода из события window
, например, путем изменения размера окна браузера, еще одна особенность JavaScript: возможность программирования «фабрик функций», то есть функций, генерирующих другие функции, возвращающих их с помощью return
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var Contexto= { prueba:“objeto”, // La propiedad prueba es de los objetos Contexto llamar: function() { esto=this; return function() { console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba); }; } } var prueba=“global”; // Esta variable prueba es global (window.prueba) var hora=Date.now(); var probador=Object.create(Contexto); var cosa=probador.llamar(); var pesadez=setInterval(cosa,3000); setTimeout(function(){clearInterval(pesadez)},30100); // Desactivar la llamada periódica |
В приведенном выше примере кода метод llamar()
де лос объекты Contexto
Он не выполняет всю работу, а возвращает анонимную функцию, которая об этом позаботится. Чтобы убедиться, что все работает как положено, существует глобальная переменная с тем же именем, что и свойство, которое функция отображает в консоли; Если контекст правильный, будет отображаться значение свойства, а не глобальной переменной.
JavaScript Попробуйте исправить знаки точки с запятой, которые мы опускаем в конце предложений. Это позволяет использовать непринужденный стиль письма, но это палка о двух концах, с которой следует обращаться осторожно. В большинстве случаев, чтобы избежать нежелательных эффектов, которые это приводит к выражениям, занимающим несколько строк, вы можете использовать круглые скобки или ставить перед ними скобки. JavaScript интерпретирует код; Вот почему строка 8 примера включает function
в задней части return
, если бы я использовал другую строку, смысл был бы совсем другим. На мой взгляд, наиболее читаемым решением является использование промежуточной (необязательной) переменной, как в следующей версии; Очевидно, что как только поведение понятно, решение принимается программистом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var Contexto= { prueba:“objeto”, // La propiedad prueba es de los objetos Contexto llamar: function() { esto=this; var variable_auxiliar= function() { console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba); }; return variable_auxiliar; } } var prueba=“global”; // Esta variable prueba es global (window.prueba) var hora=Date.now(); var probador=Object.create(Contexto); var cosa=probador.llamar(); var pesadez=setInterval(cosa,3000); setTimeout(function(){clearInterval(pesadez)},30100); |
В том же смысле, что и оценка выражения как функции, то есть возвращение функции, а не значения, которое возвращает функция; в строке 21 последнего примера (это была строка 19 предыдущего) он останавливается на clearInterval
функция, вызываемая с помощью setInterval
. Чтобы он действовал в течение 30 секунд, остановка откладывается с помощью setTimeout
, которому, в свою очередь, нужна функция в качестве первого аргумента; для доставки выполнения в качестве параметра clearInterval
с переменной, содержащей периодический вызов (а не функцию clearInterval
) — это то, для чего создана анонимная функция в последней строке.
Выбор между написанием кода, интегрирующего определение функции, более компактным (как в строке 21) или использованием вспомогательной переменной, на мой взгляд более читабельной (как в строках 19 и 20) мало различается по производительности и больше зависит от стиля и читабельности обслуживание.
Для тестирования кода перед размещением данных на сервере можно использовать генератор случайных значений в нужном диапазоне или подготовить таблицы с контролируемыми значениями, имитирующими работу в нужных условиях. В следующем примере используется простой генератор данных для всего диапазона, поэтому они кажутся немного преувеличенными.
Для тестирования вы можете скачать полный код примера формируется веб-страницей, написанной на HTML, стиль CSS и код JavaScript. Последнее наиболее актуально, так как остальные компоненты имеют лишь минимальную поддержку, очень упрощены и гораздо более подробно описаны в статьях в соответствующих разделах.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!DOCTYPE html> <html lang=“es”> <head> <meta charset=“utf-8”> <title>Temperatura</title> <script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script> <link rel=“stylesheet” href=“estilo.css” type=“text/css” media=“all”> </head> <body onload=“iniciar_grafico(‘grafico’,’valor’,’fecha’);”> <div id=“temperatura”> <div id=“ultimo_valor”> <div id=“fecha”></div> <div id=“valor”></div> </div> <div id=“grafico”></div> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | body { margin:0px 0px 0px 0px; } #ultimo_valor { box–sizing:border–box; width:100%; height:40px; padding:8px 0px 8px 14px; font–family:monospaced; font–size:18px; color:#205587; background–color:#86A7D0; } #fecha { float:left; margin–right:15px; } #valor { float:left; } #grafico { width:100%; height:200px; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | var Grafico= { contenedor_svg:void(0), // Objeto SVG con el que se dibuja el gráfico. Se inicializa con void(0) para asegurarse de que es === undefined al principio contenedor_html:void(0), // Objeto HTML que contiene al objeto SVG con el gráfico (seguramente un div). Se inicializa con void(0) para asegurarse de que es === undefined al principio contenedor_valor:void(0), // Objeto HTML en el que se muestra el último valor cargado del servidor contenedor_fecha:void(0), // Objeto HTML para mostrar la fecha del último valor pagina_servidor:“ultimo_valor_sensor.php”, // página en la que se consultan los datos consulta:“fr1”, // Valor del parámetro con el que se consulta el servidor. La URL sería algo como http://servidoriot.com/ultimo_valor_sensor.php?zona=fr1 NS:“http://www.w3.org/2000/svg”, // Nombre del espacio de nombres (name space NS) MINIMO:0, // Índice del menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia OPTIMO:1, // Índice del mejor valor del parámetro medido. Valor deseado para el parámetro monitorizado. Sirve para tener una idea rápida de cómo de correcto es el estado del sistema MAXIMO:2, // Índice del mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia maximo_puntos:0, // Número máximo de vértices representados (pueden dibujarse menos si se recortan por la parte izquierda) valor:[], // Últimos valores cargados del servidor trazado:[], // Trazados representados (en principio se trata de representar uno con relleno y otro con línea) cerrado:true, // Cuando vale true se dibuja el perfil y el área (la línea del gráfico y una zona rellena debajo). Cuando vale false solo se dibuja la línea simbolo:[], // Objetos SVG utilizados para marcar los vértices de la línea (círculos) linea_referencia:[], // Líneas horizontales en los valores de referencia texto_referencia:[], // Objetos texto que rotulan los valores de referencia fondo_referencia:[], // Rectángulos bajo los textos de los valores de referencia medida_caja_svg:[100.0,100.0], // Ancho,Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones ya que se dimensiona al 100% de su contenedor correccion_caja_svg:[1.0,1.0], // Coeficientes para calcular la medida en el gráfico de un objeto del que se sabe el valor absoluto (Se usa el píxel como unidad) tiempo_representado:0, // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado) valor_referencia:[0,0,0], // Valores de referencia [mínimo,óptimo,máximo] margen_valor:0, // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo escala_valor:[1.0,1.0], // Coeficientes que multiplican el valor monitorizado (y almacenado en el servidor) para calcular el representado según las dimensiones del gráfico desplazamiento_valor:[0.0,0.0], // Desplazamiento que se suma al tiempo (X) y al valor (Y) para representarlo en el rango especificado para el gráfico area_bajo_linea:true, // True para dibujar una zona rellena además del perfil (línea) del gráfico medida_simbolo:[5,5], // Valor absoluto (en píxeles) del ancho y el alto del dibujo que se hace en los vértices de la línea del gráfico tipografia:“sans-serif”, // Tipo de letra usado para rotular el gráfico (se usa solamente una tipografía por uniformidad) altura_texto:10.0, // Altura de los textos en valor absoluto (píxeles). No todos los navegadores soportan todos los valores intermedios, será necesario coordinarlo con los valores que estén relacionados escala_horizontal_texto:1, // Transformación que se aplica al texto para corregir la que implica la proporción del gráfico color_fondo:“”, // Color de fondo del contenedor HTML del objeto SVG (Si es una cadena vacía no se cambia) color_simbolos:“#000000”, // Color de los símbolos que se dibujan en los vértices de la línea del gráfico color_referencia:[“#000000”,“#000000”,“#000000”], // Color de la referencia que indica el valor [mínimo,óptimo,máximo] color_tipografia:“#FFFFFF”, // Color del tipo de letra con que se rotulan las referencias grosor_trazado:4, // Grosor de la línea del gráfico color_trazado:“#000000”, // Color de la línea del gráfico color_relleno:“#000000”, // Color del relleno del gráfico opacidad_relleno:1.0, // Opacidad del relleno del gráfico grosor_referencia:0.5, // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles) opacidad_referencia:1.0, // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella relleno_fondo_referencia:7.0, // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico) margen_fondo_referencia:10.0, // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico) ancho_fondo_referencia:50.0, // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto texto_valor:[“temperatura: “,” °C”], // Prefijo y sufico con los que se rotula el valor monitorizado texto_hora:[“hora “,“”], // Prefijo y sufijo con los que se rotula la hora (cuando solamente se rotula la hora) texto_fecha:[“”,“”], // Prefijo y sufijo con los que se rotula la fecha (cuando solamente se rotula la fecha) texto_fecha_hora:[“el “,” a las “,” | “], // Prefijo con el que se rotula la hora, separador de fecha y hora y sufjo de la hora cuando se rotula la fecha y la hora HORA:0, // Constante que representa la hora para el modo de la fecha/hora FECHA:1, // Constante que representa la fehca para el modo de la fecha/hora FECHA_Y_HORA:2, // Constante que representa rotular la fecha y la hora (para el modo de fecha/hora) modo_fecha:2, // 0->hora, 1-> fecha 2-> fecha y hora repetir:null, // Función llamada por setInterval nueva_proporcion: // Calcular la nueva proporción (al iniciar y al redimensionar el contenedor del gráfico) para transformar el texto function() { if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto { if(this.contenedor_svg.nodeName===“svg”) // Si el objeto asignado es un SVG { this.escala_horizontal_texto=parseFloat(getComputedStyle(this.contenedor_svg).height)/parseFloat(getComputedStyle(this.contenedor_svg).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño } } }, nueva_correccion_caja_svg: function() { if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto { if(this.contenedor_svg.nodeName.toLowerCase()===“svg”) // Si el objeto asignado es un SVG { this.correccion_caja_svg[0]=this.medida_caja_svg[0]/parseFloat(getComputedStyle(this.contenedor_svg).width); this.correccion_caja_svg[1]=this.medida_caja_svg[1]/parseFloat(getComputedStyle(this.contenedor_svg).height); } } }, nueva_escala: function() { if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero { this.escala_valor[0]=this.medida_caja_svg[0]/this.tiempo_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X this.escala_valor[1]=this.medida_caja_svg[1]/(Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y } }, nuevo_desplazamiento: function() { if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero { this.desplazamiento_valor[0]=this.valor[this.valor.length–1][0]–this.tiempo_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado this.desplazamiento_valor[1]=this.margen_valor–this.valor_referencia[this.MINIMO]; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido) } }, crear_svg: // Crear el objeto SVG dentro del objeto HTML function() { if(this.contenedor_html!==null&&this.contenedor_html!==undefined) // Ya se ha asignado el objeto HTML { if(this.contenedor_html.nodeType===1) // Es un objeto HTML válido { // Color de fondo del objeto HTML if(this.color_fondo!==“”) { this.contenedor_html.style.backgroundColor=this.color_fondo; } // Contenedor SVG this.contenedor_svg=document.createElementNS(this.NS,“svg”); // Crear un objeto SVG this.contenedor_svg.setAttribute(“width”,“100%”); // Ancho del objeto SVG this.contenedor_svg.setAttribute(“height”,“100%”); // Alto del objeto SVG this.contenedor_svg.setAttribute(“viewBox”,“0 0 “+this.medida_caja_svg[0]+” “+this.medida_caja_svg[1]); // Zona del SVG que se muestra this.contenedor_svg.setAttribute(“preserveAspectRatio”,“none”); // No preservar la proporción para ocupar todo el objeto HTML que hace de contenedor this.contenedor_html.appendChild(this.contenedor_svg); // Añadir al contenedor HTML el objeto SVG (que será el contenedor del gráfico) // Trazado this.trazado[0]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el relleno this.trazado[0].style.fill=this.color_relleno; this.trazado[0].style.fillOpacity=this.opacidad_relleno; this.trazado[0].style.stroke=“none”; this.contenedor_svg.appendChild(this.trazado[0]); // Añadir el relleno al SVG this.trazado[1]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el perfil this.trazado[1].setAttribute(“vector-effect”,“non-scaling-stroke”); // No mantener la proporción en el trazado (no deformarlo) this.trazado[1].style.fill=“none”; this.trazado[1].style.stroke=this.color_trazado; this.trazado[1].style.strokeWidth=this.grosor_trazado; //this.trazado[1].style.strokeOpacity=1.0; // La opacidad por defecto es 1.0 this.contenedor_svg.appendChild(this.trazado[1]); // Añadir el perfil al SVG // Símbolos para los vértices var ahora=Date.now()–30000; // Fecha y hora actual menos 30 segundos for(var contador_vertices=0;contador_vertices<this.maximo_puntos;contador_vertices++) { this.valor[contador_vertices]=[]; this.valor[contador_vertices][0]=ahora; // Inicializar los valores a la fecha y hora actual menos 30 segundos this.valor[contador_vertices][1]=this.valor_referencia[this.OPTIMO]; // Inicializar los valores al óptimo this.simbolo[contador_vertices]=document.createElementNS(this.NS,“ellipse”); // Crear una elipse (círculo) para cada vértice this.simbolo[contador_vertices].style.fill=this.color_simbolos; this.simbolo[contador_vertices].style.fillOpacity=1.0; this.simbolo[contador_vertices].style.stroke=“none”; this.contenedor_svg.appendChild(this.simbolo[contador_vertices]); // Añadir el símbolo al SVG } //this.nuevo_desplazamiento(); // Necesario si se asignaran las alturas de las referencias for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++) { // Línea de referencia //var posicion_corregida=this.medida_caja_svg[1]-(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Necesario si se asignaran las alturas de las referencias this.linea_referencia[contador_referencia]=document.createElementNS(this.NS,“line”); // Crear la línea que representa el valor mínimo this.linea_referencia[contador_referencia].style.stroke=this.color_referencia[contador_referencia]; this.linea_referencia[contador_referencia].style.strokeWidth=this.grosor_referencia; this.linea_referencia[contador_referencia].style.strokeOpacity=this.opacidad_referencia; this.linea_referencia[contador_referencia].setAttribute(“x1”,0.0); // Las líneas de referencia empiezan en el borde izquierdo… this.linea_referencia[contador_referencia].setAttribute(“x2”,this.medida_caja_svg[0]); // …y terminan en el derecho //this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo] //this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo] this.contenedor_svg.appendChild(this.linea_referencia[contador_referencia]); // Rectángulo de fondo del texto que indica el valor de referencia this.fondo_referencia[contador_referencia]=document.createElementNS(this.NS,‘rect’); this.fondo_referencia[contador_referencia].style.fill=this.color_referencia[contador_referencia]; this.fondo_referencia[contador_referencia].style.fillOpacity=1.0; this.fondo_referencia[contador_referencia].style.stroke=“none”; this.contenedor_svg.appendChild(this.fondo_referencia[contador_referencia]); // Texto de referencia this.texto_referencia[contador_referencia]=document.createElementNS(this.NS,“text”); // Crear el texto para rotular la referencia this.texto_referencia[contador_referencia].setAttribute(“font-family”,this.tipografia); // Asignar el tipo de letra this.texto_referencia[contador_referencia].setAttribute(“fill”,this.color_tipografia); // Asignar el color //this.texto_referencia[contador_referencia].textContent=””+(this.valor_referencia[contador_referencia]>=0?”+”:””)+this.valor_referencia[contador_referencia]; // Texto que se rotula (los valores máximo, óptimo y mínimo ya deben estar asignados) this.contenedor_svg.appendChild(this.texto_referencia[contador_referencia]); } } } }, ajustar_prporcion: function() { this.nueva_proporcion(); this.nueva_correccion_caja_svg(); var posicion_corregida; for(var contador_vertices=0;contador_vertices<this.simbolo.length;contador_vertices++) { this.simbolo[contador_vertices].setAttribute(“rx”,this.medida_simbolo[0]*this.correccion_caja_svg[0]); this.simbolo[contador_vertices].setAttribute(“ry”,this.medida_simbolo[1]*this.correccion_caja_svg[1]); } for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++) { posicion_corregida=this.medida_caja_svg[1]–(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1]; this.fondo_referencia[contador_referencia].setAttribute(“x”,this.margen_fondo_referencia*this.correccion_caja_svg[0]); this.fondo_referencia[contador_referencia].setAttribute(“y”,posicion_corregida–(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]/2.0); this.fondo_referencia[contador_referencia].setAttribute(“width”,this.ancho_fondo_referencia*this.correccion_caja_svg[0]); this.fondo_referencia[contador_referencia].setAttribute(“height”,(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]); this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida); this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida); this.texto_referencia[contador_referencia].setAttribute(“transform”,“scale(“+this.escala_horizontal_texto+“,1.0)”); this.texto_referencia[contador_referencia].setAttribute(“font-size”,this.altura_texto*this.correccion_caja_svg[1]); this.texto_referencia[contador_referencia].setAttribute(“x”,(this.margen_fondo_referencia+this.relleno_fondo_referencia)*this.correccion_caja_svg[0]/this.escala_horizontal_texto); this.texto_referencia[contador_referencia].setAttribute(“y”,posicion_corregida+this.altura_texto/2.0*this.correccion_caja_svg[1]); this.texto_referencia[contador_referencia].textContent=(this.valor_referencia[contador_referencia]>=0?“+”:“”)+this.valor_referencia[contador_referencia]; // Texto que se rotula } }, rotar_valores: function() { for(var contador_valor=0;contador_valor<this.valor.length–1;contador_valor++) { this.valor[contador_valor][0]=this.valor[contador_valor+1][0]; this.valor[contador_valor][1]=this.valor[contador_valor+1][1]; } }, nuevo_valor_aleatorio: function() { this.rotar_valores(); this.valor[this.valor.length–1][0]=this.valor[this.valor.length–2][0]+1000+Math.random()*1000; // El nuevo tiempo es el anterior más un segundo más un valor entre 0 y un segundo this.valor[this.valor.length–1][1]=Math.random()*Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.valor_referencia[this.MINIMO]; // Un valor aleatorio entre el máximo y el mínimo this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal this.dibujar_nuevo_valor(true); this.dibujar_nuevo_valor(false); this.rotular_nuevo_valor(); this.ritular_nueva_fecha(); }, cargar_nuevo_valor: function() { var consulta=‘zona=”+this.consulta; var resultado; var ajax; if(window.XMLHttpRequest) { ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest); } else // Versiones antiguas de MS Internet Explorer { ajax=new ActiveXObject(“Microsoft.XMLHTTP”); } ajax.onreadystatechange= function() { if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”) { resultado=JSON.parse(ajax.responseText); if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1]) { this.rotar_valores(); this.valor[this.valor.length–1][0]=resultado.fecha; this.valor[this.valor.length–1][1]=resultado.temperatura; this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal this.dibujar_nuevo_valor(true); this.dibujar_nuevo_valor(false); this.rotular_nuevo_valor(); this.ritular_nueva_fecha(); } } } ajax.open(“POST”,this.pagina_servidor); ajax.setRequestHeader(“Method”,“POST “+this.pagina_servidor+” HTTP/1.1″); ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”); ajax.setRequestHeader(“Content-length”,consulta.length); ajax.setRequestHeader(“Connection”,“close”); ajax.send(consulta); }, rotular_nuevo_valor: function() { var valor_redondeado=Math.round(this.valor[this.valor.length–1][1]*100.0)/100.0; this.contenedor_valor.innerHTML=this.texto_valor[0]+valor_redondeado+this.texto_valor[1]; }, ritular_nueva_fecha: function() { var fecha_lectura=new Date(this.valor[this.valor.length–1][0]); var texto_fecha=this.texto_hora[0]; texto_fecha+=(fecha_lectura.getHours()<10?“0”:“”)+fecha_lectura.getHours(); texto_fecha+=“:”; texto_fecha+=(fecha_lectura.getMinutes()<10?“0”:“”)+fecha_lectura.getMinutes(); texto_fecha+=“:”; texto_fecha+=(fecha_lectura.getSeconds()<10?“0”:“”)+fecha_lectura.getSeconds(); texto_fecha+=this.texto_hora[1]; this.contenedor_fecha.innerHTML=texto_fecha; }, dibujar_nuevo_valor: // Dibujar el gráfico cuando llega un nuevo valor (que estará almacenado en el vector valor function(cerrado) { // Si cerrado es undefined se evalúa a false, que se toma como valor por defecto var contador_valor=this.valor.length–1; // Variable para recorrer los valores (índice) var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos) var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG if(cerrado) // Si el trazado que se está dibujando está cerrado (no confundir con this.cerrado que determina si se dibujan los dos trazados, la línea y el relleno) { coordenadas_trazado+=this.medida_caja_svg[0]+“,”+this.medida_caja_svg[1]+” L “; // …se empieza por la parte inferior de la caja } while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico… { posicion[0]=(this.valor[contador_valor][0]–this.desplazamiento_valor[0])*this.escala_valor[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal posicion[1]=this.medida_caja_svg[1]–(this.valor[contador_valor][1]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG) this.simbolo[contador_valor].setAttribute(“cx”,posicion[0]); this.simbolo[contador_valor].setAttribute(“cy”,posicion[1]); coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo… { coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que representar, añadir una nueva línea (código L) para el próximo contador_valor—; // Pasar al siguiente valor } else // Si se ha rebasado el margen izquierdo… { sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas) } } if(cerrado) // Si se dibuja un trazado (path) cerrado… { coordenadas_trazado+=” L “+posicion[0]+“,”+this.medida_caja_svg[1]+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG } this.trazado[cerrado?0:1].setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado for(;contador_valor>=0;contador_valor—) { this.simbolo[contador_valor].setAttribute(“cx”,–10000); this.simbolo[contador_valor].setAttribute(“cy”,0); } } } var temperatura_frigorifico; function iniciar_grafico(nombre_objeto_grafico,nombre_objeto_valor,nombre_objeto_fecha) { temperatura_frigorifico=Object.create(Grafico); temperatura_frigorifico.contenedor_html=document.getElementById(nombre_objeto_grafico); temperatura_frigorifico.contenedor_valor=document.getElementById(nombre_objeto_valor); temperatura_frigorifico.contenedor_fecha=document.getElementById(nombre_objeto_fecha); temperatura_frigorifico.color_fondo=“#A8C3EA”; temperatura_frigorifico.color_trazado=“#205587”; temperatura_frigorifico.grosor_trazado=3; temperatura_frigorifico.color_relleno=“#205587”; temperatura_frigorifico.opacidad_relleno=0.5; temperatura_frigorifico.color_simbolos=“#205587”; temperatura_frigorifico.color_referencia[Grafico.MINIMO]=“#621D87”; temperatura_frigorifico.color_referencia[Grafico.OPTIMO]=“#1D8762”; temperatura_frigorifico.color_referencia[Grafico.MAXIMO]=“#871E35”; temperatura_frigorifico.grosor_referencia=1.5; temperatura_frigorifico.opacidad_referencia=0.5; temperatura_frigorifico.tipografia=“monospaced”; temperatura_frigorifico.color_tipografia=“#A8C3EA”; temperatura_frigorifico.cerrado=true; // Dibujar una línea y una zona rellena debajo (true por defecto) temperatura_frigorifico.maximo_puntos=32; // Un espacio de 30 segundos representados con un intervalo mínimo de 1 segundo necesitan como máximo 31 puntos, se añade uno por seguridad (por si algún valor monitorizado estuviera por debajo del segundo) temperatura_frigorifico.crear_svg(); temperatura_frigorifico.nueva_correccion_caja_svg(); temperatura_frigorifico.tiempo_representado=30000; temperatura_frigorifico.valor_referencia[Grafico.MINIMO]=–5; temperatura_frigorifico.valor_referencia[Grafico.OPTIMO]=5; temperatura_frigorifico.valor_referencia[Grafico.MAXIMO]=10; temperatura_frigorifico.margen_valor=3; temperatura_frigorifico.nueva_escala(); temperatura_frigorifico.nuevo_desplazamiento(); // El desplazamiento se (re)calcula cada vez que se añade un valor temperatura_frigorifico.ajustar_prporcion(); window.addEventListener(“resize”,function(){temperatura_frigorifico.ajustar_prporcion()}); // Versión moderna temperatura_frigorifico.repetir=setInterval(function(){temperatura_frigorifico.nuevo_valor_aleatorio()},1000); } |
Оставить комментарий