Paint
Научившись работать с Raphaël и пользуясь познаниями из прошлой статьи плагин нужно соединить с colorpicker'ом и datagrid'ом. Долго не гугля я выбрал два кажущиеся мне нормальные плагина и похоже не ошибся. Интеграция прошла быстро и безболезненно :)
Задача: есть некая модель данных, которая должна отображаться в двух видах: в табличке в виде данных и на картинке ввиде линии. Линии можно выделять при этом реагирует табличка подсвечивая выбранный ряд, соответственно и наоборот если выбрать ряд в табличке надо подсветить линию на рисунке, линии можно удалять, удаляя данные и из таблички, и добавлять добавляя пустой ряд в табличку. Так вроде все описал, на всякий случай еще приделал к этому цветовую палитру, еще наверное надо будет толщину линии выбирать разрешить, но это просто.
Модель данных взята от фонаря три шага налево...
var model = [
{id : 0, amount : 100, units : "meters", value : 13, color : "#00f", path : "M 250 73 L 250 73"},
{id : 1, amount : 200, units : "meters", value : 22, color : "#0f0", path : "M 327 67 L 327 67"},
{id : 2, amount : 300, units : "meters", value : 36, color : "#f00", path : "M 185 172 L 185"},
{id : 3, amount : 400, units : "meters", value : 18, color : "#00f", path : "M 216 231 L 216"},
{id : 4, amount : 150, units : "meters", value : 23, color : "#f00", path : "M 274 171 L 274 171"},
{id : 5, amount : 360, units : "liters", value : 36, color : "#00f", path : "M 308 176 L 308 176"},
{id : 6, amount : 170, units : "liters", value : 12, color : "#0f0", path : "M 358 113 L 358 113"},
{id : 7, amount : 180, units : "liters", value : 24, color : "#f00", path : "M 201 109 L 201 109"},
{id : 8, amount : 292, units : "liters", value : 37, color : "#00f", path : "M 228 114 L 228 114"},
{id : 9, amount : 182, units : "kgrams", value : 43, color : "#0f0", path : "M 357 245 L 357 245"},
{id : 10,amount : 273, units : "kgrams", value : 53, color : "#f00", path : "M 346 172 L 346 172"},
{id : 11,amount : 164, units : "kgrams", value : 61, color : "#00f", path : "M 386 179 L 386 179"},
{id : 12,amount : 254, units : "kgrams", value : 27, color : "#0f0", path : "M 218 178 L 218 178"}
];
Загадочная надпись M 250 73 L 250 73 это начало линии в формате SVG, дословно она означает Move To 250 px OX 73 px OY Line To 250 px OX 73 px OY, если в конце написать "z" то начало и конец линии замкнуться. В примере я укоротил запись в исходниках все по честному.
Теперь это все надо отрендерить, для этого я написал следующий код, который вы можете использовать как есть, но использовать пока не советую из-за крайней сырости, но за найденные баги буду очень благодарен
(function($) {
/**
* author: CTAPbIu_MABP
* email: ctapbiumabp@gmail.com
* site: http://mabp.kiev.ua/
* last update: 08.12.2009
* version: 0.2
*/
var Paint = function (node, options) {
$.extend(this, options);
this.init(node);
};
Paint.prototype = {
// private
raphael : null,
isStarted : false,
node : null,
offset : null,
// public
color : "#f00",
defaultColor : "#000",
// callbacks
onInit : function() {
},
onLineClick : function() {
},
onLineDblClick : function() {
},
onLineDelete : function() {
},
onStart : function() {
},
onStop : function() {
},
onMove : function() {
},
init : function (raw_img) {
var img = $(raw_img).hide(),
self = this,
width = img.width(),
height = img.height();
this.node = img.wrap("<div/>").parent().addClass("plot").css({width:width,height:height});
this.raphael = new Raphael(this.node.get(0), width, height);
this.raphael.image(img.attr("src"), 0, 0, width, height);
this.unselectable(this.node.get(0));
this.node.mouseover(function() {
//this.style.cursor = 'pointer';
//$(this).css({'cursor':'url("pencil.cur")'});
}).mousedown(function(e) {
self.start(e);
}).mouseup(function(e) {
self.stop(e);
}).mousemove(function(e) {
self.move(e);
});
this.node.bind("drawLine setColor deleteLine", function(e, param) {
self[e.type](param);
});
var model = this.model;
this.model = [];
for (var i = 0,j = model.length; i < j; i++) {
this.setColor(model[i].color);
this.drawLine(model[i].path);
}
this.onInit.apply(this, [raw_img]);
},
setColor : function(color) {
this.color = /^#([a-f0-9]{3}){1,2}$/i.test(color) ? color : this.defaultColor;
},
start: function(e) {
//console.log("start");
e.preventDefault(); // prevent drag image
if (this.isStarted)
return;
this.offset = this.node.offset();
this.model.push({
line : "M " + (e.pageX - this.offset.left) + " " + (e.pageY - this.offset.top),
color : this.color,
path : null
});
this.isStarted = true;
this.onStart.apply(this, [e]);
},
move: function(e) {
//console.log("move");
if (!this.isStarted) return;
var model = this.model.pop();
model.path && model.path.remove();
delete model.path,model;
this.drawLine(model.line + " L " + (e.pageX - this.offset.left) + " " + (e.pageY - this.offset.top));
this.onMove.apply(this, [e]);
},
stop: function(e) {
//console.log("finish");
this.isStarted = false;
var model = this.model.pop();
e.isClick = !model.path; // was it line or just click on image
model.path && this.model.push(model) || delete model;
this.onStop.apply(this, [e]);
},
drawLine : function(line) {
var path = this.raphael.path(line)
.attr({stroke:this.color,'stroke-width':5,'stroke-linejoin':'round'});
var self = this;
$(path.node)
.click((function(c) {return function() {self.onLineClick.apply(self, [c]);}})(self.model.length))
.dblclick((function(c) {return function() {self.onLineDblClick.apply(self, [c]);}})(self.model.length))
.mouseover(function() {this.style.cursor = 'crosshair';});
this.model.push({line:line,path:path,color:this.color});
},
deleteLine : function(index) {
var model = this.model[index];
model.path && model.path.remove();
delete model.path, model;
this.onLineDelete.apply(this, [index])
},
unselectable: function(element) {
// http://ajaxcookbook.org/disable-text-selection/
element.onselectstart = function() {
return false;
};
element.unselectable = "on";
element.style.MozUserSelect = "none";
}
};
$.fn.paint = function(options) {
this.each(function() {
new Paint(this, options);
});
return this;
};
})(jQuery);
Простите, но комментариев к коду пока нет, будут в следующей версии, наверное, сильно не обольщайтесь. Да и скачать этот код можно отсюда.
Теперь пора показать как это все собрать в кучу, запускаю плагин.
$("#plot").paint({
model : model,
// текущий подсвеченный элемент
hightlited : null, // it may be zero
hightlitedColor : "#fff",
// обработчик двойного клика на линию
// удаляет подсветку
// удаляет линию с рисунка
// удаляет ряд из таблицы
onLineDblClick : function(index){
this.hightlited = null;
this.deleteLine(index);
$("#grid").jqGrid('delRowData',index+1);
},
// обработчик клика на линию
onLineClick : function (index) {
// снимает прошлуб подсветку если была
if (this.hightlited != null){
var prev = this.model[this.hightlited];
prev.path.attr({stroke:prev.color});
}
// добавляет новую
this.model[index].path.attr({stroke:this.hightlitedColor});
// подсвечивает ряд в таблице
$("#grid").jqGrid('setSelection',index+1);
this.hightlited = index;
},
// call-back на событие остановки рисования линии
onStop : function(e){
// удаляет старую подсветку если была
if (this.hightlited != null){
var prev = this.model[this.hightlited];
prev.path.attr({stroke:prev.color});
}
// если это не случайный клик
if (!e.isClick){
// подсвечивает свежесозданную линию
var index = this.model.length-1;
this.model[index].path.attr({stroke:"#fff"});
this.hightlited = index;
// добавляет ряд в таблицу
$("#grid").jqGrid('addRowData',index+1,this.model[index]);
}
},
// пока не забыл надо в самом начале определить
// событие которое будет вызвано когда в таблице
// поменяется выделенная строка
onInit : function(){
var self = this;
this.node.bind("hightliteLine", function(e, index) {
// убирает старую подсветку
if (self.hightlited != null){
var prev = self.model[self.hightlited];
prev.path.attr({stroke:prev.color});
}
// подсвечивает новую линию
self.model[index].path.attr({stroke:self.hightlitedColor});
self.hightlited = index;
});
}
});
Теперь надо привязать выбор цвета, для этого создается небольшая функция инициализирующая colorpicker, когда colorpicker меняет цвет срабатывает событие у рисовалки, и она меняет цвет линии.
$('#colorpicker').ColorPicker({
flat: true,
// начальный цвет
color: '#00ff00',
// вся магия тут
onChange: function(hsb, hex, rgb) {
$("#plot").trigger("setColor","#"+hex);
}
});
Еще немного магии для инициализации data grid'а :
$("#grid").jqGrid({
datatype: "local",
height: 250,
colNames:['No','Amount','Units','Value'],
colModel:[
{name:'id',index:'id',width:100, sorttype:"int"},
{name:'amount',index:'amount', width:100, sorttype:"int", align:"right"},
{name:'units',index:'units', width:100, sorttype:"float"},
{name:'value',index:'value', width:100, sorttype:"int", align:"right"}
],
caption: "Lines Data",
onSelectRow : function (id){
$("#plot").trigger("hightliteLine", id-1);
}
});
for(var i=0, j=model.length;i<=j;i++)
$("#grid").jqGrid('addRowData',i+1,model[i]);
Тут, помимо общих настроек и установки модели, есть чудо метод который вызовет событие подсветки линии когда измениться текущий ряд в таблице.
Я думаю, я в ближайшее время не заброшу этот плагин так что ждите новую версию, возможно даже с документацией, пишите свои каменты, не забывайте читать мои бредни ;)
Свежие комментарии