Рисуем в браузере. Как попробовать функции браузера Edge в Хроме

Писать красивый код - одно удовольствие, но тяжело делиться этим удовольствием с другими программистами, не говоря уже о тех, кто таковыми не является. В свободное от работы и общения с семьей время, я вынашивал идею о том, как же спрограммировать поэму, используя элементы canvas для того, чтобы рисовать в браузере.

Существует большое количество терминов, которые описывают визуальные эксперименты на компьютере схема кода, демо, интерактивное искусство и так далее. Для того, чтобы описать этот процесс, я остановился на термине поэма программирования. Поэма - это отполированная до блеска часть прозы, легкая, краткая и эстетичная. Это не полузаконченный набросок идеи в альбоме, а нечто связное, представленное зрителю для его удовольствия. Поэма - это не инструмент, но она живет, чтобы вызвать эмоцию.

Я перечитал много книг посвященных математике, вычислительной технике, физике и биологии для своего удовольствия. И я очень быстро понял то, что если я распространяюсь об какой-либо идее, людям это быстро надоедает. На самом деле, я могу взять идею, которую считаю невероятно интересной и быстро заразить человека чувством заинтересованности, даже если человек не имеет никакого представления о кодировании и о том, как это вообще работает. Тебе не нужно хвататься за какую-то то сложную философскую мысль или за вычислительные расчеты для того, чтобы спрограммировать поэму. Необходимо лишь желание увидеть нечто-то живое и обладающее дыханием у себя на экране. Коды и примеры, которые я оставляю ниже, помогут вам понять, как фактически осуществить этот быстрый и несложный процесс.

Главная фишка в том, что создание поэмы должно оставаться простым и легким. Не нужно тратить три месяца, и строить один, хоть и очень классный демо. Вместо этого создайте 10 поэм, которые помогут воплотить идею в жизнь. Напишите код, который будет захватывающим, и не бойтесь потерпеть неудачу.

Введение в Canvas

Коротко говоря, канвас - это 2-х мерный растровый элемент изображения, который находится в DOM (объектная модель документа) и там же может быть прорисован. Рисовать можно как в 2d контексте, так и в контексте WebGL. Контекстом является JavaScript, который мы используем для того, чтобы получить доступ к инструментам рисования. Процессы JavaScript, которые доступны для канвас, очень понятные, в отличие от доступных для SVG. Любой процесс, вызванный для элемента в целом, а не что-то нарисованное в канвас, точно такой же, как и обычный элемент изображения. Вот базовый пример канвас:

Var canvas = document.getElementById("example-canvas"); var context = canvas.getContext("2d"); //Draw a blue rectangle context.fillStyle = "#91C0FF"; context.fillRect(100, // x 100, // y 400, // width 200 // height); //Draw some text context.fillStyle = "#333"; context.font = "18px Helvetica, Arial"; context.textAlign = "center"; context.fillText("The wonderful world of canvas", // text 300, // x 200 // y);

Здесь нет ничего сложного, можно легко начать работу. Единственное, что может немного смутить, это контекст, который должен формироваться с параметрами настройки, такими как fillStyle, lineWidth, font и strokeStyle, прежде чем фактический рисунок будет использоваться. Можно легко забыть обновить или перезагрузить параметры настройки и в итоге получить непредвиденный результат.

Создаем движение

Первый пример только бегает и превращает статичный рисунок в канвас. Все это хорошо, но вот настоящее веселье начинается тогда, когда ты задаешь скорость 60 кадров в секунду. У современных браузеров есть надстройка в функции requestAnimationFrame, которая синхронизирует код исходного рисунка с рисунком, который движется в браузере. Это улучшает эффективность и придает плавность движения. Целью визуализации должен быть код, который движется со скоростью 60 кадров в секунду.

var canvas = document.getElementById("example-canvas");

Var context = canvas.getContext("2d"); var counter = 0; var rectWidth = 40; var rectHeight = 40; var xMovement; //Place rectangle in the middle of the screen var y = (canvas.height / 2) - (rectHeight / 2); context.fillStyle = "#91C0FF"; function draw() { //There are smarter ways to increment time, but this is for demonstration purposes counter++; //Cool math below. More explanation in the text following the code. xMovement = Math.sin(counter / 25) * canvas.width * 0.4 + canvas.width / 2 - rectWidth / 2; //Clear the previous drawing results context.clearRect(0, 0, canvas.width, canvas.height); //Actually draw on the canvas context.fillRect(xMovement, y, rectWidth, rectHeight); //Request once a new animation frame is available to call this function again requestAnimationFrame(draw); } draw();

Математическое рисование

А теперь будьте внимательны, до этого мы говорили о программировании и рисовании на мониторе, а здесь я подготовил нечто математическое. Признаюсь, я всегда любил математику (в отличие от многих других художников). Несмотря на эту любовь, я разочарован тем, как ее преподают в школе. Ее преподают слишком формально, что может быть и хорошо для преподавателя математики, но при этом теряется восхищение, интерес к исследованию и прагматизм, с помощью которых можно сделать много всего интересного. Но есть и хорошая новость, программисты не обращают на это внимания и концентрируются на более интересных вещах.

А теперь я постараюсь быстро объяснить, как работает система координат. Это будет полезно для того, чтобы понять, как математическая функция может создать движение. Картинка ниже показывает интерактивную систему координат. Обратите внимание, что там не только координата х. Функция, которую я нарисовал, представлена как (a*x+b)*c+d. Поиграйте слайдером по графику, и вы увидите, как каждая из этих значений может регулировать положение графика и масштаб.

А теперь я перепишу свою формулу по коду-образцу, который был у нас раньше и который будет легче читаться.

Var a = 1 / 25, //Make the oscillation happen a lot slower x = counter, //Move along the graph a little bit each time draw() is called b = 0, //No need to adjust the graph up or down c = width * 0.4, //Make the oscillation as wide as a little less than half the canvas d = canvas.width / 2 - rectWidth / 2; //Tweak the position of the rectangle to be centered xMovement = Math.sin(a * x + b) * c + d;

Ввод данных

Отложив пока то, что мы сделали, подумайте несколько минут о том, что вы можете сделать с различными устройствами ввода данных пользователем, чтобы переместить квадрат в пределах страницы. Есть много вариантов, доступных в браузере включая микрофон, веб-камеру, мышь, клавиатуру и геймпад. Могут быть доступны дополнительные управляемые плагином варианты, к примеру, Leap Motion или Kinect. Используя WebSockets и сервер Вы можете связать визуализацию со сделанными в домашних условиях техническими средствам (имеются в виду подручные?). Соедините микрофон к Web Audio API и ведите свои звуковые пиксели. Вы можете даже построить датчик движения из Веб-камеры и испугать стаю виртуальных рыб (я делал такое в последний раз во Flash приблизительно пять лет назад).

Теперь, когда у вас есть своя идея, давайте рассмотрим еще несколько примеров. Один квадрат это скучно, давайте вставим побольше квадратов. Но прежде всего, создадим функцию (систему координат), которая нам еще во многом пригодится. Назовем ее Точка. Нюанс, который поможет нам с работой движущихся объектов состоит в том, что нужно использовать вектор отличный от х и y. Я собрал коды-образцы в three.js Vector2 class. Легче работать с вектором х и вектором y, но есть еще множество удобных вариантов. Для того, чтобы лучше понять о чем речь, загляните сюда. (здесь должна быть ссылка).

Всемогущая точка

Эти примеры будут немного сложнее, потому что они связаны с объектами, но это того стоит. Посмотрите образцы кода, что бы понять как работает new Scene который поможет с основами рисования в канвас. У нашей новой Точки будет маркер, чтобы иметь доступ к любой переменной, как и в канвас контексте.

Function Dot(x, y, scene) { var speed = 0.5; this.color = "#000000"; this.size = 10; this.position = new THREE.Vector2(x,y); this.direction = new THREE.Vector2(speed * Math.random() - speed / 2, speed * Math.random() - speed / 2); this.scene = scene; }

Для того, чтобы начать работу с конструктором, для Точки настраивают конфигурацию ее работы и устанавливают некоторые переменные, необходимые для работы. И опять мы обращаемся к three.js vector. Когда идет воспроизведение в кадровой частоте на 60fps, важно заранее запустить ваши предметы и не создавать новые во время воспроизведения. Это может съесть имеющуюся память и сделать вашу визуализацию обрывистой. Также, обратите внимание на то, как Точка передает копию кадра по ссылке. Это помогает сработать четче.

Dot.prototype = { update: function() { ... }, draw: function() { ... } }

Вся остальная часть кода будет установлена на объекте прототипа Точки так, чтобы у каждой новой Точки был доступ к этим методам. Я буду объяснять от функции к функции.

Update: function(dt) { this.updatePosition(dt); this.draw(dt); },

Я отделяю код рисования от кода обновления. Это помогает намного легче поддерживать и двигать объект. Прямо как схема MVC разделяет твой личный контроль и логический взгляд. Вектор dt - это изменение во времени в миллисекундах с момента выхода последнего обновления. Это название удобное и короткое и происходит от (не пугайтесь) calculus derivatives (производная расчета). Он вычитает скорость вашего движения от скорости смены кадров. Таким образом, у вас не будет замедления стиля NES, если возникнут какие-то сложности. Если ваша скорость будет слишком высокая, получится так, что будут пропускаться кадры, но скорость останется та же.

UpdatePosition: function() { //This is a little trick to create a variable outside of the render loop //It"s expensive to allocate memory inside of the loop. //The variable is only accessible to the function below. var moveDistance = new THREE.Vector2(); //This is the actual function return function(dt) { moveDistance.copy(this.direction); moveDistance.multiplyScalar(dt); this.position.add(moveDistance); //Keep the dot on the screen this.position.x = (this.position.x + this.scene.canvas.width) % this.scene.canvas.width; this.position.y = (this.position.y + this.scene.canvas.height) % this.scene.canvas.height; } }(), //Note that this function is immediately executed and returns a different function

Эта функция немного странная по своей структуре, но удобная в пользовании. Действительно дорого распределять память в системе координат (функции). MoveDistance можно установить один раз и использовать при повторном запуске системы функции, когда это необходимо.

Этот вектор используется только для того, что рассчитать новую позицию и не предназначена для работы вне функции. Это первый математический вектор, который стали использовать. Сейчас направление вектора умножается в противовес изменению во времени, а затем прибавленное к позиции. В конце происходит действие модуля, который удерживает точку на экране.

Draw: function(dt) { //Get a short variable name for convenience var ctx = this.scene.context; ctx.beginPath(); ctx.fillStyle = this.color; ctx.fillRect(this.position.x, this.position.y, this.size, this.size); }

И в конце самое легкое. Сделайте копию контекста главного объекта и нарисуйте прямоугольник (или что-нибудь другое, не важно). Просто прямоугольник проще всего нарисовать на экране.

На этом этапе я добавляю новую Точку и называю ее this.dot = new Dot(x, y, this) в основном конструкторе. А затем, в основном методе обновления я добавляю this.dot.update(dt) и появляется точка движущаяся по экрану.

Приятно, что в коде немного больше структуры, но это не придает большей привлекательности. Здесь начинается цикл. В основном объекте мы создадим новый объект DotManager. Удобно собрать эту функциональность в отдельном объекте, поскольку это легче и более чисто, потому что к моделирование становится все больше сложным.

Var DotManager = function(numberOfDots, scene) { this.dots = ; this.numberOfDots = numberOfDots; this.scene = scene; for(var i=0; i < numberOfDots; i++) { this.dots.push(new Dot(Math.random() * this.canvas.width, Math.random() * this.canvas.height, this.scene)); } }; DotManager.prototype = { update: function(dt) { for(var i=0; i < this.numberOfDots; i++) { this.dots[i].update(dt); } } };

Теперь в шаблоне, вместо того, чтобы обновить Точку, мы создаем и обновляем DotManager. Для начала создадим 5000 точек.

Function Scene() { ... this.dotManager = new DotManager(5000, this); ... }; Scene.prototype = { ... update: function(dt) { this.dotManager.update(dt); } ... };

Это, определенно, выглядит намного лучше, чем одна точка. Теперь самое время для того, чтобы начать добавлять больше к обновленному методу в вашей Точке. Любое изменение в коде объекта отразится на каждой точке на экране. А вот здесь и начинают происходить чудеса. Помните систему координат расположенную выше? Как на счет того, чтобы задать эффект волнообразного движения? Мы создадим переменную wavePosition к объекту Точка. В заключение, мы добавим эту переменную к позиции Y.

UpdateWave: function(dt, time) { this.wavePosition = Math.sin(this.scene.currTime / 500 + this.position.x / this.scene.canvas.width * 4) * 20; }

Да, немного сбивает с толку, когда написано в одну строку, поэтому прикладываю другой вариант, который сделан как в системе координат.

Var a = 1 / 500, //Make the oscillation happen a lot slower x = this.scene.currTime, //Move along the graph a little bit each time draw() is called b = this.position.x / this.scene.canvas.width * 4, //No need to adjust the graph up or down c = 20, //Make the oscillation as wide as a little less than half the canvas d = 0; //Tweak the position of the rectangle to be centered xMovement = Math.sin(a * x + b) * c + d;

Я волнуюсь…
Еще один небольшой штрих. Монохромность немного скучна, поэтому добавим немного цвета.

Var hue = this.position.x / this.scene.canvas.width * 360; this.color = Utils.hslToFillStyle(hue, 50, 50, 0.5);

Для каждой новой Точки задайте ее начальную позицию и установите оттенок по горизонтали канваса. Я добавил функцию Utils.hslToFillStyle , она немного поможет преобразовать некоторые вводимые переменные в правильно отформатированной fillStyle цепочке. Дела уже идут намного интереснее. Точки в итоге будут собираться в одном месте и уже не будет эффекта радуги после того как они будут хаотично разбросаны. И опять же, это пример подвижной визуализации с примесью математики или вводными значениями. Мне больше нравится работать с цветом через цветовую модель HSL, чем с RGB, потому что она легче в использовании. RGB немного абстрактна.

Работа с мышью

До этого момента не было реальной работы пользователя.

Var Mouse = function(scene) { this.scene = scene; this.position = new THREE.Vector2(-10000, -10000); $(window).mousemove(this.onMouseMove.bind(this)); }; Mouse.prototype = { onMouseMove: function(e) { if(typeof(e.pageX) == "number") { this.position.x = e.pageX; this.position.y = e.pageY; } else { this.position.x = -100000; this.position.y = -100000; } } };

Этот простой объект заключает в себе обновление для работы с мышью, в отличие от остальной части работы. Обновление состоит в том, что позиция вектора совпадает с позицией движущейся мыши. Остальная часть объектов может отличаться от позиции вектора мыши, если на них есть ссылка в объекте. Одна оговорка, которую я оставил без внимания. Если ширина канваса не идет один в один с размерами пикселя DOM, т.е. в ответ на изменение размера канваса или на высокую плотность пикселей (ретина), или если канвас не расположен в верхнем левом углу. Координаты мыши должны быть изменены соответственно.

Var Scene = function() { ... this.mouse = new Mouse(this); ... };

Остается только создать объект мыши внутри схемы. Теперь, когда мы имеем мышь можно сделать так, что точки будут следовать за ней.

Function Dot(x, y, scene) { ... this.attractSpeed = 1000 * Math.random() + 500; this.attractDistance = (150 * Math.random()) + 180; ... }

Я добавил несколько скалярных величин к точке, так что каждая ведет себя отлично от остальных, что придает процессу большую реалистичность. Поиграйте с этими значениями и вы почувствуете разницу. Теперь все внимание на метод мыши. Получается немного длинно с комментариями.

AttractMouse: function() { //Again, create some private variables for this method var vectorToMouse = new THREE.Vector2(), vectorToMove = new THREE.Vector2(); //This is the actual public method return function(dt) { var distanceToMouse, distanceToMove; //Get a vector that represents the x and y distance from the dot to the mouse //Check out the three.js documentation for more information on how these vectors work vectorToMouse .copy(this.scene.mouse.position) .sub(this.position); //Get the distance to the mouse from the vector distanceToMouse = vectorToMouse.length(); //Use the individual scalar values for the dot to adjust the distance moved moveLength = dt * (this.attractDistance - distanceToMouse) / this.attractSpeed; //Only move the dot if it"s being attracted if(moveLength > 0) { //Resize the vector to the mouse to the desired move length vectorToMove .copy(vectorToMouse) .divideScalar(distanceToMouse) .multiplyScalar(moveLength); //Go ahead and add it to the current position now, rather than in the draw call this.position.add(vectorToMove); } }; }()

Этот метод может немного сбить вас с толку, если вы не очень хорошо разбираетесь в векторной математике. Векторы могут быть хорошо визуализированы и могут помочь нарисовать каракули на бумаге с пятном от кофе. Проще говоря, это функция показывает расстояние между мышкой и точкой. И когда точка движется приближаясь к другой точке, которая основывается на том, насколько она уже близка к другой точке и на том, сколько времени прошло. Это делается путем вычисления расстояния движения (обычная скалярная цифра), и увеличивает эту цифру, нормализуя вектор точки (вектор длиной 1) совпадающей с мышкой. Ладно, последнее предложение не обязательно было писать простым языком, но ведь это только начало.

Самым важным моментом для меня является процесс создания поэмы программирования и искусство создания чего-то нового. Я бы советовал вам сильно не углубляться в детали и большие проекты. Вместо этого повторяйте, экспериментируйте, радуйтесь успеху и пусть иногда что-то у вас не получится. Однажды у меня получилось нечто похожее на кусок жуткой радуги. Я возлагал большие надежды на рекурсивные круги, но я не позволил плохим результатам сбить меня с моего пути. В конечном счете, код, который здесь был подробно описан - это исследование в очень объектно-ориентированном стиле, который мне больше всего нравится. Приспосабливайте эти идеи своему собственному процессу, инструментам и технике. Поэмы не являются большой интегрированной системой, поэтому ее легко рисовать и экспериментировать с ней не беря в расчет условия больших проектов.

Рассматривайте канвас как карандаш, который поможет вам воплотить ваши замечательные и идеи и даст возможность вашим творениям полететь.

Существует множество терминов, с помощью которых описываются визуальные эксперименты на компьютере, такие как dev art , code sketch , demo и интерактивное искусство, но в конечном итоге я выбрал термин программная поэма. Идея поэмы заключается в том, что это должна быть отшлифованная проза, легко передаваемая, краткая и эстетичная.

Это не должна быть незавершенная идея, но законченный фрагмент, который доставляет удовольствие от просмотра. Поэма не является инструментом, ее задача — вызывать эмоции.

Для собственного удовольствия я читал книги по математике, вычислительным алгоритмам, физике и биологии. Очень быстро я понял, что, если просто носиться с идеей, это скоро надоест людям.

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

Вам не нужна какая-то сложная философия или математика, чтобы написать программную поэму, только желание увидеть что-то живое и яркое на экране.

Код и примеры, которые я собрал ниже, помогут вам понять, как осуществляется этот не слишком продолжительный, но очень насыщенный процесс. Если вы хотите разбираться параллельно с кодом, вы можете скачать исходные файлы здесь .

Основная сложность при создании поэмы — все должно быть просто и легко. Не тратьте три месяца на создание одного действительно классного демо. Вместо этого создайте 10 поэм, которые развивают идею. Напишите экспериментальный код, который будет впечатляющим, захватывающим, и не бойтесь потерпеть неудачу.

Введение по холстам

Если вкратце, холст по существу представляет собой двухмерный растровый элемент изображения, существующий в DOM , с помощью которого его можно нарисовать.

Рисунок может быть создан либо с помощью 2d -контекста, либо с помощью WebGL -контекста. Контекст является объектом JavaScript , который вы используете, чтобы получить доступ к инструментам рисования. События JavaScript , которые доступны для холста, очень кратки, в отличие от SVG .

Любое возникающее событие относится ко всему элементу в целом, а не какому-нибудь отдельному элементу, нарисованному на холсте, как в обычных элементах изображений. Вот простой пример холста:

var canvas = document.getElementById("example-canvas"); var context = canvas.getContext("2d"); //Рисование синего прямоугольника context.fillStyle = "#91C0FF"; context.fillRect(100, // x 100, // y 400, // ширина 200 // высота); //Рисование текста context.fillStyle = "#333"; context.font = "18px Helvetica, Arial"; context.textAlign = "center"; context.fillText("The wonderful world of canvas", // text 300, // x 200 // y);

С ним довольно просто начать работу. Единственное, что может быть немного сложным, это контекст, для которого перед запуском непосредственно рисования должны быть заданы такие настройки, как стили заполнения, ширина линии, шрифт и стили штрихов.

Очень легко забыть, что эти настройки должны быть обновлены или восстановлены, и тогда вы получите неожиданные результаты.

Приведение элементов в движение

В первом примере прямоугольник просто пробегается один раз через весь экран и остается в виде статического изображения на холсте. Это неплохо, но анимация действительно приносит удовольствие, когда картинка обновляется с частотой 60 кадров в секунду.

Современные браузеры имеют встроенную функцию requestAnimationFrame , которая синхронизирует пользовательский код рисования с циклом рисования браузера. Это помогает в плане эффективности и качества. Целью визуализации должен быть код, который работает с частотой 60 кадров в секунду.

(Замечание относительно поддержки браузерами: Есть несколько простых polyfills, если вам нужно обеспечить поддержку в старых браузерах ):

var canvas = document.getElementById("example-canvas"); var context = canvas.getContext("2d"); var counter = 0; var rectWidth = 40; var rectHeight = 40; var xMovement; //Помещаем прямоугольник в центр экрана var y = (canvas.height / 2) - (rectHeight / 2); context.fillStyle = "#91C0FF"; function draw() { //Существует более интеллектуальный способ увеличения времени, однако для демонстрации достаточно и этого counter++; //Ниже приводится крутая математика. Более подробное пояснение приведено ниже по тексту статьи. xMovement = Math.sin(counter / 25) * canvas.width * 0.4 + canvas.width / 2 - rectWidth / 2; //Очищаем ранее нарисованный результат context.clearRect(0, 0, canvas.width, canvas.height); //Непосредственно рисуем на холсте context.fillRect(xMovement, y, rectWidth, rectHeight); //Запрошенный однажды новый фрейм анимации может вызвать эту функцию снова requestAnimationFrame(draw); } draw();

Рисование с помощью математики

Теперь будьте внимательны, мы говорили о программировании и рисовании на экране, поэтому я перехожу к математике. Математика — это то, что создает магию, которая потом отображается на экране. Если честно, я всегда любил математику (а вот многие художники нет ).

Несмотря на эту любовь, я был несколько разочарован тем, как ее преподавали в школе. Все это было очень формально, что, конечно, отлично подходит математикам, но в этом не хватало волнения, глубокого исследования и прагматизма, что могло бы помочь в изучении некоторых очень простых концепций. Однако программисты игнорируют абстрактные доказательства и сосредотачиваются на интересных приложениях.

Позвольте мне отнять несколько секунд вашего времени, чтобы пояснить, как работает функция синуса. Это будет полезно, в плане того, чтобы задуматься, как с помощью математических функций можно создавать движение. Приведенные ниже миниатюры являются ссылками на интерактивный график функции синуса.

Обратите внимание, что у нас есть больше переменных, чем просто х в функции sin(x) . Функция, которую я создал — это sin(a * x + b) * c + d . Поиграйте с ползунками на интерактивном графике, и вы сможете увидеть, как изменение любого из этих значений влияет на график функции.

Теперь я перепишу формулу из предыдущего примера кода и создам другую версию, которую будет легче читать:

var a = 1 / 25, //Делаем так, чтобы колебания графика были намного реже x = counter, //Сдвигаем немного график каждый раз, когда вызывается draw() b = 0, //Нет необходимости сдвигать график вверх или вниз c = width * 0.4, //Задаем ширину волны немного меньше, чем половина холста d = canvas.width / 2 - rectWidth / 2; //Передвигаем прямоугольник в центр xMovement = Math.sin(a * x + b) * c + d;

Если вы хотите поэкспериментировать с тем кодом, что я уже показал, я бы советовал вам попробовать добавить перемещение вдоль оси у. Попробуйте изменить значения в функции синуса или возьмите другую тригонометрическую функцию, чтобы поэкспериментировать с ней и посмотреть, что происходит.

Ввод данных пользователем

По ходу рассмотрения движения с помощью математики, хочу воспользоваться моментом и прикинуть, что вы можете сделать с помощью различных устройств ввода для перемещения прямоугольника по странице. Браузеры поддерживают все возможные варианты ввода, в том числе микрофон, веб-камеру, мышь, клавиатуру и геймпад.

Возможно также подключить дополнительные варианты ввода с помощью плагинов наподобие Leap Motion или Kinect . Используя WebSockets и сервер, вы можете подключить к визуализации свое оборудование.

Подключите микрофон к Web Audio API , и вы сможете управлять пикселями с помощью звука. Вы даже можете создать на базе веб-камеры датчик движения и распугивать виртуальных рыбок (что-то подобное я сделал на Flash пять или около того лет назад ).

Теперь, когда мы разобрались с этим, давайте вернемся обратно и рассмотрим еще несколько примеров. Один квадрат это скучно, давайте поднимем ставки. Во-первых, давайте создадим функцию квадрата, который сможет делать многие вещи. Мы назовем ее Dot .

Еще одна вещь, которая здорово помогает при работе с движущимися объектами, это векторы, вместо использования отдельных переменных х и у .

В этих примерах кода, которые я подтянул из Three.js, есть класс Vector2 . Намного проще использовать сразу vector.x и vector.y , но этот класс также содержит несколько удобных методов. Для получения более подробной информации вы можете ознакомиться с документацией .

Всемогущая Dot

В этом примере все уже немного сложнее, потому что это взаимодействия с объектами, но оно того стоит. Посмотрите пример кода, в котором вы увидите новый объект Scene, который управляет рисованием на холсте.

Наш новый класс Dot будет работать с этим объектом, чтобы получить доступ к переменным, которые ему будут нужны, например, к контексту холста:

function Dot(x, y, scene) { var speed = 0.5; this.color = "#000000"; this.size = 10; this.position = new THREE.Vector2(x,y); this.direction = new THREE.Vector2(speed * Math.random() - speed / 2, speed * Math.random() - speed / 2); this.scene = scene; }

Чтобы приступить к разработке конструктора для Dot , настраиваем конфигурацию его поведения и устанавливаем некоторые переменные, которые мы будем использовать. Опять же, с помощью класса векторов Three.js .

При выводе содержимого с частотой 60 кадров в секунду, важно предварительно инициализировать объекты, а не создавать новые во время анимации. Потому что это съедает ресурсы памяти и может привести к прерывистой работе анимации. Кроме того, обратите внимание, как в Dot через ссылку передается копия scene . Это помогает поддерживать чистоту кода:

Все остальные части кода будут установлены в прототипе объекта Dot таким образом, чтобы каждый новый создаваемый Dot имел доступ к этим методам. Ниже я разберу все функции одну за другой:

Dot.prototype = { update: function() { ... }, draw: function() { ... } }

Я обновляю код рисования из обновленного кода. Таким образом, значительно проще поддерживать код и увеличивать производительность объекта. Точно также в шаблоне проектирования MVC выделяется управление и логика представления. Переменная dt начинает изменяться каждую миллисекунду с момента последнего вызова обновлений.

Название хорошее, короткое и происходит от (не пугайтесь ) производных исчислений. Это отделяет перемещение от скорости смены кадров. Таким образом, вы избежите замедления стиля NES , когда все станет слишком сложным. Если нагрузка будет слишком большой, из анимации выпадут некоторые кадры, но скорость останется той же самой:

updatePosition: function() { //Это небольшая хитрость для создания переменной вне цикла визуализации //Это требует больших затрат ресурса памяти внутри цикла. //Переменная доступна только для приведенной ниже функции. var moveDistance = new THREE.Vector2(); //Это собственно функция return function(dt) { moveDistance.copy(this.direction); moveDistance.multiplyScalar(dt); this.position.add(moveDistance); //Сохраняем dot в пределах экрана this.position.x = (this.position.x + this.scene.canvas.width) % this.scene.canvas.width; this.position.y = (this.position.y + this.scene.canvas.height) % this.scene.canvas.height; } }(), //Обратите внимание, что данная функция выполняется немедленно и возвращает разные функции

Эта функция имеет немного запутанную структуру, но она удобна для визуализации. Она действительно потребляет много памяти. Переменная moveDistance устанавливается один раз и может повторно использоваться в любое время при вызове функции.

Этот вектор используется только, чтобы помочь вычислить новое положение, но не используется за пределами функции. Это первый элемент векторной математики, который мы использовали.

Сейчас вектор направления умножается на каждое изменение во времени, а затем добавляется к позиции. В конце концов, мы производим модульные действия, чтобы сохранить dot в пределах screen:

draw: function(dt) { //Для удобства получаем короткое имя переменной var ctx = this.scene.context; ctx.beginPath(); ctx.fillStyle = this.color; ctx.fillRect(this.position.x, this.position.y, this.size, this.size); }

Дальше все просто. Получаем копию контекста из объекта scene , а затем рисуем прямоугольник (или что вы хотите ). Прямоугольник — это фигура, которую, вероятно, быстрее всего можно нарисовать на экране.

Теперь я добавляю новый Dot через вызов this.dot = new Dot(x, y, this) в конструкторе scene, а затем в методе обновления основного пространства действия добавляю this.dot.update(dt) и получаю масштабирование точки по всему экрану. (Чтобы увидеть весь код в контексте, посмотрите исходный код к статье ).

Это хорошо, что у нас есть небольшая внутренняя конструкция с кодом, но она на самом деле не делает ничего особенно интересного. А вот цикл — это намного интереснее. В объекте рабочего пространства мы создадим новый объект DotManager .

Удобнее, если мы соберем этот функционал в отдельный объект, чтобы код был проще и чище, так как сам процесс у нас все больше усложняется:

var DotManager = function(numberOfDots, scene) { this.dots = ; this.numberOfDots = numberOfDots; this.scene = scene; for(var i=0; i < numberOfDots; i++) { this.dots.push(new Dot(Math.random() * this.canvas.width, Math.random() * this.canvas.height, this.scene)); } }; DotManager.prototype = { update: function(dt) { for(var i=0; i < this.numberOfDots; i++) { this.dots[i].update(dt); } } };

Теперь вместо того, чтобы создавать и обновлять Dot , мы используем scene, чтобы создавать и обновлять DotManager . Для начала мы создадим 5000 точек:

function Scene() { ... this.dotManager = new DotManager(5000, this); ... }; Scene.prototype = { ... update: function(dt) { this.dotManager.update(dt); } ... };

Хорошо, это уже лучше, чем просто одна точка. Сейчас самое время приступить к добавлению кода к методу обновления для одиночной Dot . Все меняется, когда код объекта влияет на каждую точку на экране. Вот когда на вещи начинает действовать магия. Помните функцию синуса? Как насчет того, чтобы добавить сейчас небольшой волновой эффект?

Мы создадим переменную wavePosition для объекта Dot . В финальном процессе рисования мы добавим эту переменную к позиции относительно оси Y :

updateWave: function(dt, time) { this.wavePosition = Math.sin(this.scene.currTime / 500 + this.position.x / this.scene.canvas.width * 4) * 20; }

Небольшое затруднение в одной из строк, код ломается, как ранее функция синуса:

var a = 1 / 500, //Задаем, чтобы волновой эффект происходил немного медленнее x = this.scene.currTime, //Сдвигаем немного вдоль графика, каждый раз когда вызывается draw() b = this.position.x / this.scene.canvas.width * 4, // Нет необходимости сдвигать график вверх или вниз c = 20, //Задаем ширину волны немного меньше, чем половина холста d = 0; //Передвигаем прямоугольник в центр xMovement = Math.sin(a * x + b) * c + d;

Получилось очень увлекательно …

Еще одна маленькая фишка. Монохромная палитра выглядит уныло, давайте добавим немного цвета:

var hue = this.position.x / this.scene.canvas.width * 360; this.color = Utils.hslToFillStyle(hue, 50, 50, 0.5);

Для каждой вновь создаваемой Dot берем ее первоначальную позицию, и устанавливаем для нее оттенок, в зависимости от того, где она расположена по ширине холста. Utils.hslToFillStyle — это небольшая вспомогательная функция, которую я добавил, чтобы превратить некоторые входные переменные в корректно отформатированные строки FillStyle .

Теперь все выглядит еще более захватывающим. Опять же, это пример управления визуализацией с помощью математики или переменных ввода. Я действительно получаю удовольствие от создания цветов с помощью цветовой модели HSL , а не RGB , из-за ее простоты. RGB немного абстрактен.

Взаимодействие с пользователем через мышь

До сих пор у нас не было никакого реального взаимодействия с пользователем:

var Mouse = function(scene) { this.scene = scene; this.position = new THREE.Vector2(-10000, -10000); $(window).mousemove(this.onMouseMove.bind(this)); }; Mouse.prototype = { onMouseMove: function(e) { if(typeof(e.pageX) == "number") { this.position.x = e.pageX; this.position.y = e.pageY; } else { this.position.x = -100000; this.position.y = -100000; } } };

Этот простой объект инкапсулирует логику обновления через действия мыши из рабочей области. Он только обновляет вектор позиции при перемещении мыши. Остальные объекты могут после этого определить положение вектора мыши, если им переданы ссылки на объект.

Одно замечание. Я не рассматриваю здесь случай, когда ширина холста не соответствует один к одному размерам DOM в пикселях, т.е. адаптивно подстраиваемый холст, высокую плотность пикселей (сетчатки ) холста, или когда холст не начинается в верхнем левом углу. В этих случаях координаты мыши должны быть соответствующим образом скорректированы:

var Scene = function() { ... this.mouse = new Mouse(this); ... };

Единственное, что нам осталось сделать, это создать объект мыши внутри рабочей области. Теперь, когда мы ввели взаимодействие с мышью, давайте создадим эффект притягивания к ней точек:

function Dot(x, y, scene) { ... this.attractSpeed = 1000 * Math.random() + 500; this.attractDistance = (150 * Math.random()) + 180; ... }

Поиграйте с этими значениями, чтобы изменить эти эффекты. Теперь займемся методом притягивания точек к мыши. Это немного длинный код с комментариями:

attractMouse: function() { //Опять, создаем частные переменные для этого метода var vectorToMouse = new THREE.Vector2(), vectorToMove = new THREE.Vector2(); //Это собственно общий метод return function(dt) { var distanceToMouse, distanceToMove; //Получаем вектор, который отображает расстояние по оси x и y от точки до мыши //Чтобы узнать больше о том, как работают эти векторы, ознакомьтесь с документацией three.js vectorToMouse .copy(this.scene.mouse.position) .sub(this.position); //Получаем расстояние от мыши до вектора distanceToMouse = vectorToMouse.length(); //Используем отдельные скалярные значения, чтобы отрегулировать дистанцию перемещения moveLength = dt * (this.attractDistance - distanceToMouse) / this.attractSpeed; //Перемещаем точку, только если он притянулась if(moveLength > 0) { //Изменяем размер вектора для мыши на нужную длину перемещения vectorToMove .copy(vectorToMouse) .divideScalar(distanceToMouse) .multiplyScalar(moveLength); //Продолжаем и добавляем его к текущей позиции сейчас, а не при вызове рисования this.position.add(vectorToMove); } }; }()

Этот метод может показаться вам сложным, если вы не очень хорошо разбираетесь в векторной математике. Векторы могут помочь в создании визуальных представлений, если вы хотите сделать что-то похожее на след от чашки кофе на листе бумаги.

На простом языке, эта функция получает расстояние между мышью и точкой. А затем она перемещает точку совсем близко к мыши, в зависимости от того, как близко она уже была до этого и попала ли в зону действия притяжения, а также от того, сколько времени прошло.

Это делается через вычисление расстояния для перемещения (обычное скалярное число ) и затем его умножение на соответствующий вектор (вектор с длиной 1 ), который направлен от точки к мыши. Да, последнее предложение трудно отнести к простому языку.

Для меня суть программной поэмы и генерации художественных элементов заключается в исследовании различных возможностей. Я бы рекомендовал вам не слишком увлекаться деталями или крупными проектами.

Вместо того чтобы попробовать несколько более мелких проектов, поэкспериментировать с ними и добиться определенного результата, вас может ожидать провал и разочарование.

Как-то в конце одной из моих работ я пришел к тому, что все выглядело, как кусок разноцветного мусора. А я ведь возлагал на этот проект большие надежды. Но я не позволяю неудачам сломить меня.

Приведенный в этой статье код много раз переписывался, чтобы добиться объектно-ориентированного стиля, который я считаю наиболее подходящим.

Адаптируйте приведенные здесь идеи под ваш собственный рабочий процесс и инструменты. Поэма это не большая интегрированная система, таким образом, с ней просто экспериментировать без ограничений связанных с громоздкими веб-проектами.

Применяйте свои холсты, как листы бумаги, на которых вы рисуете карандашом. Найдите интересные идеи, поэкспериментируйте с ними. И не ограничивайте полет фантазии.

Если на твоем сайте выложена фотка любимого кота Мурзика, но ты не хочешь, чтобы кто-то ее копировал или сохранял на хард, то поставь такой скрипт в тело сайта.

Технология Drag & Drop (перетащи и отпусти) используется практически в любой прикладной программе. Иногда (например при разработке JS игр) данную технологию надо…

На первый взгляд, элемент - это элемент HTML5, который можно использовать для рисования. Он представляет собой лишь контейнер, в котором можно рисовать…

.

Всем привет! Сегодня я хотел бы познакомить вас с семейством приложений Monosnap. Эти приложения разработаны для быстрого создания скриншотов. Сейчас в линейке присутствуют приложения для Mac OS X, Windows, iPad, iPhone, и, с недавнего времени, расширение для Google Chrome. Расширение реализует большую часть функционала десктопных версий.

В основном, расширение рассчитано на людей, постоянно занимающихся интернет-серфингом. Вам может понравится очередная картинка с 9gag или же вы наткнулись на интересный дизайн сайта и решили сохранить его для дальнейшего использования в своем проекте. В любом случае имеет смысл использовать расширение для браузера вместо десктопного приложения.

В расширении доступно 4 режима снятия скриншота:

1) Capture Selected Area - стандартный снимок выделенной области веб-страницы;
2) Capture Visible Part Of The Page - скриншот видимой части страницы;
3) Capture Full Page - снимок веб-страницы в полную высоту;
4) Smart Capture - самый интересный режим из всех, позволяет делать снимок HTML-блока, на который вы навели курсор.

Редактирование

После создания скриншота предлагается отредактировать его или же сразу загрузить в облачное хранилище. В редакторе пользователь может сделать акценты на определенных областях и оставить надписи/комментарии.

Инструменты:

1) Ручка (brush) - позволяет рисовать кривые и делать подписи.
2) Линия (line), эллипс (ellipse), прямоугольник (rectangle) - примитивы, служащие для выделения ключевых фрагментов скриншота
3) Размытие изображения (blur) - размывает изображение и тем самым делает конфиденциальную информацию нечитабельной.
4) Стрелка (arrow) - примитив в виде стрелки, предназначенный для акцентирования внимания на чем-либо.
5) Текст (text) - для надписей и пометок.
6) Надпись (bubble) - комбинация двух предыдущих инструментов (стрелка с текстом), позволяет указать на ключевой фрагмент скриншота и оставить к нему комментарий.
7) Обрезка изображения (crop) - вырезает из скриншота прямоугольную область. Весьма полезная фишка, если вам нужен не весь скриншот, а лишь его один фрагмент.
8) Палитра (colorpicker) - помимо стандартных цветов, позволяет использовать любые другие (с помощью ввода HEX, RGB или HSB значения цвета).

Помимо прочего редактор поддерживает отслеживание изменений и позволяет отменить (undo) и вернуть (redo) изменения, а также очистить скриншот, вернув его к первоначальному состоянию до редактирования (wipe).

Загрузка

Загрузка изображения в облако возможна без авторизации (в отличие от Awesome Screenshot, храниться они будут целый месяц с момента последнего просмотра), или же после входа в сервис через facebook или свой аккаунт. Что немаловажно - вы всегда можете вернуться к режиму редактирования скриншота, так что не придется делать скриншот заново.

Текущая версия расширения является лишь стартовой и будет постоянно обновляться и развиваться. Надеемся, вы найдете его полезным!