html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Создание фреймворка для Canvas: объекты и мышь



Среди вопросов на счёт Canvas чаще всего звучали вопросы по внутренностям фреймворков — как понять, что мышка находится над элементом, как это реализовано в фреймворках. В топике мы реализуем подобный Canvas фреймворк на базе AtomJS.


Глобальный интерфейс


Для начала придумаем интерфейс нашего фреймворка. Назовём его Canvas Framework, сокращённо — CF. Это будет глобальная переменная-фабрика для создания инстанса.
Первым аргументом мы будем передавать в неё ссылку на нужный элемент:

var cf = new CF('#my-canvas');


Реализация — простая:
window.CF = atom.Class({
	initialize: function (canvas) {
		this.canvas = atom.dom( canvas ).first;
		this.ctx    = this.canvas.getContext('2d');
	}
});


Затем мы можем создавать объекты, при помощи этой сущности:

cf.circle([50, 50, 10]    , { fill: 'red'  , hover : { fill: 'blue' } });
cf.rect  ([10, 10, 20, 20], { fill: 'green', hover : { fill: 'blue' } });


Для простоты все объекты у нас будут тягаться и слушать события мыши.

Реализация фигур


Теперь нам необходимо определить базовый класс фигуры.

// Абстрактный класс фигур
var Shape = atom.Class({
	Implements: [ atom.Class.Events, atom.Class.Options ],
	
	cf   : null,
	data : null,
	hover: false,
	
	path: atom.Class.abstractMethod,
	
	initialize: function (data, options) {
		this.data = data;
		this.setOptions( options );
	},
	
	hasPoint: function (x, y) {
		var ctx = this.cf.emptyCanvas.ctx;
		this.path( ctx );
		return ctx.isPointInPath(x, y);
	},
		   
	draw: function () {
		var ctx = this.cf.ctx, o = this.options;
		this.path( ctx );
		ctx.save();
		ctx.fillStyle = this.hover ? o.hover.fill : o.fill;
		ctx.fill();
		ctx.restore();
	}
});


Вы видите, что нам понадобится некий emptyCanvas — это будет скрытый Canvas, в который мы будем отрисовывать наши пути, чтобы не нарушать пути «боевого» холста. Обновим конструктор CF:

window.CF = atom.Class({
	initialize: function (canvas) {
		[...]
		this.emptyCanvas = atom.dom.create( 'canvas', { width: 1, height: 1 }).first;
		this.emptyCanvas.ctx = this.emptyCanvas.getContext('2d');
	}
});


Каждая наследующая фигура должна будет только реализовать метод path. Давайте добавим пару фигур — Rectangle и Circle

// circle.data == [x, y, radius]
var Circle = atom.Class({
	Extends: Shape,
	path: function (ctx) {
		ctx.beginPath();
		ctx.arc( this.data[0], this.data[1], this.data[2], 0, Math.PI * 2, false );
		ctx.closePath();
	}
});

var Rect = atom.Class({
	Extends: Shape,
	path: function (ctx) {
		ctx.beginPath();
		ctx.rect.apply( ctx, this.data );
		ctx.closePath();
	}
});


Следующее, что нам необходимо сделать — это реализовать Mouse. Мы подпишемся на событие mousemove элемента Canvas и будем запоминать положение курсора. Мышь будет получать элементы Shape, которые будет проверять, менять им hover и вызывать у них евенты mousedown и mouseup. Вы можете видеть, что мы столкнулись с лёгкой некроссбраузерностью — код layerX/Y нету в Опере и там необходимо использовать offsetX/Y. Не критично, но, главное, знать об этом)

var Mouse = atom.Class({
	x: 0, 
	y: 0,
	initialize: function (canvas) {
		this.elements = [];
		canvas.bind({
			mousemove: this.move.bind(this),
			mousedown: this.fire.bind(this, 'mousedown'),
			mouseup:   this.fire.bind(this, 'mouseup'  )
		});
	},
	add: function (element) {
		this.elements.push( element );
	},
	move: function (e) {
		// Мы воспользуемся layer*, но на практике нужен более надёжный способ
		if (e.layerX == null) { // opera
			this.x = e.offsetX;
			this.y = e.offsetY;
		} else { // fx, chrome
			this.x = e.layerX;
			this.y = e.layerY;
		}

		this.elements.forEach(function (el) {
			el[i].hover = el[i].hasPoint(this.x, this.y)
		}.bind(this));
	},
	fire: function (name, e) {
		this.elements.forEach(function (el) {
			if (el.hasPoint(this.x, this.y)) {
				el.fireEvent(name, e);
			}
		}.bind(this));
	}
});

// Добавляем мышь в конструктор:
window.CF = atom.Class({
	initialize: function (canvas) {
		[...]
		this.mouse = new Mouse( this.canvas );
	}
});


Теперь необходима обновлялка холста.

window.CF = atom.Class({
	initialize: function (canvas) {
		[...]
		// 25 fps
		this.update.periodical( 1000/25, this );
	},
					   
	update: function (shapes) {
		this.ctx.clearRect(0,0,this.canvas.width, this.canvas.height);
		this.elements.invoke('draw');
	}
});


Редактируем наш глобальный объект так, чтобы мы могли создавать элементы:
window.CF = atom.Class({
	[...],
	elements: [],
	_shape: function (Class, args) {
		var e = new Class(args[0], args[1]);
		this.mouse.add( e );
		this.elements.push( e );
		e.cf = this;
		return e;
	},
	circle: function (data, options) {
		return this._shape(Circle, arguments);
	},
	rect: function (data, options) {
		return this._shape(Rect, arguments);
	}
})


Всё, создаём наше приложение:

var write = function (msg) {
	atom.dom.create('p').text(msg).appendTo('body');
};

var cf = new CF('canvas');

cf.circle([50, 50, 10]    , { fill: 'red'  , hover : { fill: 'blue' } })
	.addEvent('mousedown', write.bind( window, 'circle mousedown' ));
	
cf.rect  ([10, 10, 20, 20], { fill: 'green', hover : { fill: 'blue' } })
	.addEvent('mousedown', write.bind( window, 'rect   mousedown' ));


Результат



Заключение


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

Между прочим, есть альтернативный путь — использование map + area. У него есть свои преимущества, но и недостатки. Это трудности в синхронизации и, главное, невозможность реализации более сложных фигур.

Любители потроллить на тему SVG — удержитесь. Есть причины использовать Canvas. Более того этот топик — неплох в качестве обучения. Потому попрошу хотя бы в этот раз обойтись без холивара. А то вы уже надоели
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

1

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • skypractic
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции