I have override fabricjs Text object to make letter spacing
fabric.util.object.extend(fabric.Text.prototype, {
letterSpace: 0,
_renderChars: function(method, ctx, chars, left, top) {
if(!this.letterSpace){
ctx[method](chars, left, top);
return;
}
var charShift = 0;
for(var i = 0; i < chars.length; i++){
if(i > 0){
charShift += this.letterSpace + ctx.measureText(chars.charAt(i-1)).width;
}
ctx[method](chars.charAt(i), left+charShift, top);
}
},
_getLineWidth: function(ctx, lineIndex) {
if (this.__lineWidths[lineIndex]) {
return this.__lineWidths[lineIndex];
}
var lineLength = this._textLines[lineIndex].length;
var additionalSpaceSum = 0
if(lineLength > 0){
additionalSpaceSum = this.letterSpace * (lineLength - 1);
}
this.__lineWidths[lineIndex] = ctx.measureText(this._textLines[lineIndex]).width + additionalSpaceSum;
return this.__lineWidths[lineIndex];
}
});
Spacing works good, but width is not correct, how to improve width calculation?
I have improve my code in this question and now it work fine))
Sorry for not show previous mistake, but here is too difficult to make clear cod, and I have correct previous in this question.
But I have write it for left text align, if you use different align you need correct it. For me it was enough
I have improve my code in question and now it work fine)) sorry.
But I have write it for left text align, if you use different align you need correct it. For me it was enough
fabric.util.object.extend(fabric.Text.prototype, {
letterSpace: 0,
_renderChars: function (method, ctx, chars, left, top) {
if (!this.letterSpace) {
ctx[method](chars, left, top);
return;
}
var charShift = 0;
for (var i = 0; i < chars.length; i++) {
if (i > 0) {
charShift += this.letterSpace + ctx.measureText(chars.charAt(i - 1)).width;
}
ctx[method](chars.charAt(i), left + charShift, top);
}
},
_getLineWidth: function (ctx, lineIndex) {
var lineLength = this._textLines[lineIndex].length;
var additionalSpaceSum = 0
if (lineLength > 0) {
additionalSpaceSum = this.letterSpace * (lineLength - 1);
}
this.__lineWidths[lineIndex] = ctx.measureText(this._textLines[lineIndex]).width + additionalSpaceSum;
return this.__lineWidths[lineIndex];
},
_renderExtended: function (ctx) {
this.clipTo && fabric.util.clipContext(this, ctx);
this.extendedRender = true;
this._renderTextBackground(ctx);
this._renderText(ctx);
this._renderTextDecoration(ctx);
this.clipTo && ctx.restore();
}
});
This code works for all alignments:
fabric.util.object.extend(fabric.Text.prototype, {
letterSpace: 0,
_renderChars: function (method, ctx, chars, left, top) {
if (!this.letterSpace) {
ctx[method](chars, left, top);
return;
}
var characters = String.prototype.split.call(chars, '');
if(this.textAlign == 'left'){
var charShift = 0;
for (var i = 0; i < chars.length; i++) {
if (i > 0) {
charShift += this.letterSpace + ctx.measureText(chars.charAt(i - 1)).width;
}
ctx[method](chars.charAt(i), left + charShift, top);
}
}else if(this.textAlign == 'right'){
characters.reverse();
chars = characters.join('');
var charShift = 0;
for (var i = 0; i < chars.length; i++) {
if (i > 0) {
charShift += this.letterSpace + ctx.measureText(chars.charAt(i - 1)).width;
}
ctx[method](chars.charAt(i), left - charShift, top);
}
}else if(this.textAlign == 'center'){
var totalWidth = 0;
for (var i = 0; i < characters.length; i++) {
totalWidth += (ctx.measureText(characters[i]).width + this.letterSpace);
}
var currentPosition = left - (totalWidth / 2);
var charShift = 0;
for (var i = 0; i < chars.length; i++) {
if (i > 0) {
charShift += this.letterSpace + ctx.measureText(chars.charAt(i - 1)).width;
}
ctx[method](chars.charAt(i), currentPosition + left + charShift, top);
}
}
},
_getLineWidth: function (ctx, lineIndex) {
var lineLength = this._textLines[lineIndex].length;
var additionalSpaceSum = 0
if (lineLength > 0) {
additionalSpaceSum = this.letterSpace * (lineLength - 1);
}
this.__lineWidths[lineIndex] = ctx.measureText(this._textLines[lineIndex]).width + additionalSpaceSum;
return this.__lineWidths[lineIndex];
},
_renderExtended: function (ctx) {
this.clipTo && fabric.util.clipContext(this, ctx);
this.extendedRender = true;
this._renderTextBackground(ctx);
this._renderText(ctx);
this._renderTextDecoration(ctx);
this.clipTo && ctx.restore();
}});
Here is an example: http://jsfiddle.net/peybdq94/
I tried implementing this feature and came up with this https://jsfiddle.net/ghazaltaimur/bx0f4qpg/1/ by extending _renderChar. I have made a couple of additions. The code allows letter spacing to be applied on the selected text instead of only on the whole object.Plus itext selection ,bounding box and cursor position have to be taken into account if letter spacing is to be added. I have tried to cover these aspects as well. There might be a couple of issues which still need to be fixed.
fabric.util.object.extend(fabric.IText.prototype, {
letterSpace: 0,
_renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) {
var decl, charWidth, charHeight,
offset = this._fontSizeFraction * lineHeight / this.lineHeight;
if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) {
var shouldStroke = decl.stroke || this.stroke,
shouldFill = decl.fill || this.fill;
ctx.save();
charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl);
charHeight = this._getHeightOfChar(ctx, _char, lineIndex, i);
var chars = _char;
var characters = String.prototype.split.call(chars, '');
var charShift = 0;
var leftcharShift = 0;
var letterSpace;
for (var i = 0; i < chars.length; i++) {
var style = this.getCurrentCharStyle(lineIndex, i + 1);
letterSpace = style.letterSpace;
if (i > 0) {
charShift += parseInt(letterSpace) + parseInt(ctx.measureText(chars.charAt(i - 1)).width);
}
if (this.text.indexOf(chars) !== 0 && charShift === 0) {
charShift = this.text.indexOf(chars) * parseInt(letterSpace);
}
leftcharShift = parseInt(left) + parseInt(charShift);
if (shouldFill) {
ctx.fillText(chars.charAt(i), leftcharShift, top);
}
if (shouldStroke) {
ctx.strokeText(chars.charAt(i), leftcharShift, top);
}
}
this._renderCharDecoration(ctx, decl, left, top, offset, charWidth, charHeight);
ctx.restore();
ctx.translate(charWidth, 0);
} else {
charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i);
var chars = _char;
var characters = String.prototype.split.call(chars, '');
var charShift = 0;
var leftcharShift = 0;
var letterSpace;
for (var i = 0; i < chars.length; i++) {
var style = this.getCurrentCharStyle(lineIndex, i + 1);
letterSpace = style.letterSpace;
if (i > 0) {
charShift += parseInt(letterSpace) + parseInt(ctx.measureText(chars.charAt(i - 1)).width);
}
if (this.text.indexOf(chars) !== 0 && charShift === 0) {
charShift = this.text.indexOf(chars) * parseInt(letterSpace);
}
leftcharShift = parseInt(left) + parseInt(charShift);
if (method === 'strokeText' && this.stroke) {
ctx[method](chars.charAt(i), leftcharShift, top);
}
if (method === 'fillText' && this.fill) {
ctx[method](chars.charAt(i), leftcharShift, top);
}
}
this._renderCharDecoration(ctx, null, left, top, offset, charWidth, this.fontSize);
ctx.translate(ctx.measureText(_char).width, 0);
}},
getCurrentCharStyle: function(lineIndex, charIndex) {
var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
return {
fontSize: style && style.fontSize || this.fontSize,
fill: style && style.fill || this.fill,
textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
textDecoration: style && style.textDecoration || this.textDecoration,
fontFamily: style && style.fontFamily || this.fontFamily,
fontWeight: style && style.fontWeight || this.fontWeight,
fontStyle: style && style.fontStyle || this.fontStyle,
stroke: style && style.stroke || this.stroke,
strokeWidth: style && style.strokeWidth || this.strokeWidth,
letterSpace: style && style.letterSpace || this.letterSpace
};
},
_renderTextLine: function(method, ctx, line, left, top, lineIndex) {
// to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine
// the adding 0.05 is just to align text with itext by overlap test
if (!this.isEmptyStyles()) {
top += this.fontSize * (this._fontSizeFraction + 0.05);
}
this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex);
},
_getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
if (this.textAlign === 'justify' && /\s/.test(_char)) {
return this._getWidthOfSpace(ctx, lineIndex);
}
var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
this._applyFontStyles(styleDeclaration);
var cacheProp = this._getCacheProp(_char, styleDeclaration);
var style = this.getCurrentCharStyle(lineIndex, charIndex);
var letterSpace = style.letterSpace;
if (this._charWidthsCache[cacheProp] && this.caching) {
return parseInt(this._charWidthsCache[cacheProp]) + parseInt(letterSpace);
} else if (ctx) {
ctx.save();
var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
width = parseInt(width) + parseInt(letterSpace);
ctx.restore();
return width;
}
},
});
// fabric js code
var canvas = new fabric.Canvas('fabric_canvas');
// add text
fabric.util.addListener(document.getElementById('addText'), 'click', function() {
var itext = new fabric.IText("Add text", {
left: 50,
top: 50
});
canvas.add(itext);
canvas.setActiveObject(itext);
var obj = canvas.getActiveObject();
var seletedText = obj.getSelectedText();
itext.selectAll();
itext.enterEditing();
if (obj.setSelectionStyles && obj.isEditing)
obj.setSelectionStyles({
letterSpace: 1
});
if (seletedText === "") {
obj.exitEditing();
}
canvas.renderAll();
});
// add letter spacing
fabric.util.addListener(document.getElementById('addLetterSpacing'), 'click', function() {
var activeObject = canvas.getActiveObject();
var seletedText = activeObject.getSelectedText();
if (seletedText === "") {
activeObject.selectAll();
activeObject.enterEditing();
}
if (activeObject.setSelectionStyles && activeObject.isEditing)
activeObject.setSelectionStyles({
letterSpace: 30
});
if (seletedText === "") {
activeObject.exitEditing();
}
var ctx = activeObject.ctx;
var textLines = activeObject.text.split(activeObject._reNewline);
var letterSpace = (activeObject.getSelectionStyles && activeObject.isEditing && activeObject.evented === true) ? activeObject.getSelectionStyles()["letterSpace"] : activeObject["letterSpace"];
activeObject.width = activeObject._getTextWidth(ctx, textLines, activeObject) + (activeObject.text.length * letterSpace);
activeObject.height = activeObject._getTextHeight(ctx, textLines, activeObject);
activeObject.callSuper('setCoords');
canvas.renderAll();
});
</code></pre>
[1]: https://jsfiddle.net/ghazaltaimur/bx0f4qpg/1/
Related
my Image is the and code is here i want to make nameplate of text. This is scorolble panel.
const updatePanel = (panel, content) => {
const sizer = panel.getElement("panel");
const { scene } = panel;
var nameImage, nameText;
sizer.clear(true);
const lines = content.split("\n");
for (let li = 0, lcnt = lines.length; li < lcnt; li += 1) {
const words = lines[li].split(" ");
for (let wi = 0, wcnt = words.length; wi < wcnt; wi += 1) {
nameText = sizer.add(
scene.add
.text(0, 0, words[wi], {
fontSize: 20,
fill: "#f4b331",
})
.setInteractive()
.on("pointerdown", () => {
// this.scene.print.text = this.text;
// this.setTint(Phaser.Math.Between(0, 0xf4b331));
})
);
console.log("new line")
}
if (words != '' && content != "Loading...") {
nameImage = sizer.add(scene.add.sprite(0, 0, "nameplate"));
}
if (li < lcnt - 1) {
sizer.addNewLine();
}
}
panel.layout();
return panel;
};
my Image is the and code is here i want to make nameplate of text. This is scorolble panel.
const updatePanel = (panel, content) => {
const sizer = panel.getElement("panel");
const { scene } = panel;
var nameImage, nameText;
sizer.clear(true);
const lines = content.split("\n");
for (let li = 0, lcnt = lines.length; li < lcnt; li += 1) {
const words = lines[li];
for (let wi = 0, wcnt = words.length; wi < wcnt; wi += 1) {
}
sizer.add(
scene.add
.text(0, 0, ' ', {
fontSize: 20,
fill: "#f4b331",
})
);
if (words != '' && content != "Loading...") {
nameImage = sizer.add(
new ScoreBg(
scene,
0,
0,
"nameplate",
"nameplate",
words,
"",
"",
"#f4b331"
)
// scene.add.sprite(0, 0, "nameplate"),
);
// nameImage
}
if (li < lcnt - 1) {
sizer.addNewLine();
}
}
panel.layout();
return panel;
};
//This is a touch and drag sorting code, but setInterval sometimes doesn't work after packaging
//I would be grateful if there is a reply.
function drag(preHandler){
var lastPos = { x: 0, y: 0, x1: 0, y1: 0 }; //拖拽对象的四个坐标
var tarPos = { x: 0, y: 0, x1: 0, y1: 0 }; //目标元素对象的坐标初始化
var theDivPosition;//元素位置
var mousedownPosition;
var theDiv = null,
move = false; //拖拽对象 拖拽状态
var theDivHeight = 0,
theDivWidth = 0,
tarFirstY = 0; //拖拽对象的索引、高度、的初始化。
var tarDiv = null,
tarFirst = null; //要插入的目标元素的对象, 临时的虚线对象
var tempDiv = $("<div class='dash'></div>"); //虚线框对象
var scroll;
var roll = false; //避免重复进入持续滚动
var clientYY = 0;//当move事件时改变数值,以便停止计时器
var touch = false;
$('.listItem').bind('touchstart',function(event) {
$(document.body).css({
"overflow-x":"hidden",
"overflow-y":"hidden"
});
touch = true;
console.log('touchstart');
event.stopPropagation();
theDiv = $(this);
theDivHeight = theDiv.height();
theDivWidth = theDiv.width();
theDivPosition = theDiv.offset().top - $(window).scrollTop();
mousedownPosition = event.originalEvent.targetTouches[0].clientY;
move = true;
theDiv.attr("class", "listItemdash");
theDiv.css({ top: theDivPosition});
lastPos.y = theDivPosition;
tempDiv.insertBefore(theDiv);
$(".listItemdash").css({
"height":theDivHeight,
"width":theDivWidth,
"cursor": "pointer",
"z-index": 99999
});
$(".dash").css("height",theDivHeight);
});
$('.listItem').bind('touchmove', function(event) {
touch = true;
console.log('touchmove');
if (!move) return false;
clientYY = event.originalEvent.targetTouches[0].clientY;
var body_scrillTop = $(window).scrollTop();//方法返回或设置匹配元素的滚动条的垂直位置。
var scrollHeight = $('body').height();//整体内容的高度
var windowHeight = $(window).height(); //窗口高度
function li_move(){
lastPos.y = lastPos.y + (clientYY - mousedownPosition);
lastPos.y1 = theDiv.offset().top + theDivHeight;
// 拖拽元素随鼠标移动
theDiv.css({ top: lastPos.y});
// 拖拽元素随鼠标移动 查找插入目标元素
var $listItem = $('.listItem'); // 局部变量:按照重新排列过的顺序 再次获取 各个元素的坐标,
$listItem.each(function() {
tarDiv = $(this);
tarPos.x = tarDiv.offset().left;
tarPos.y = tarDiv.offset().top;
tarPos.y1 = tarPos.y;
// console.log(tarPos.y);
tarFirst = $listItem.eq(0); // 获得第一个元素
tarFirstY = tarFirst.offset().top; // 第一个元素对象的中心纵坐标
//拖拽对象 移动到第一个位置
if (theDiv.offset().top <= tarFirstY) {
tempDiv.insertBefore(tarFirst);
}
//判断要插入目标元素的 坐标后, 直接插入
if (theDiv.offset().top >= tarPos.y - theDivHeight / 2 && lastPos.y1 >= tarPos.y1) {
tempDiv.insertAfter(tarDiv);
}
});
}
li_move();
//下滚
if((event.originalEvent.targetTouches[0].clientY > windowHeight-70) && (body_scrillTop+windowHeight < scrollHeight-30) && !roll){
roll = true;
var touchRollDown = setInterval(function(){
if((body_scrillTop+windowHeight > scrollHeight-30) || (clientYY < windowHeight-70) || move == false){
clearInterval(touchRollDown);
roll = false;
return;
}
body_scrillTop = body_scrillTop + 10;
$(window).scrollTop(body_scrillTop);
li_move();
},100);
}
//上滚
if(event.originalEvent.targetTouches[0].clientY < 70 && (body_scrillTop > 50) && !roll) {
roll = true;
var touchRollUp = setInterval(function(){
// 在滚动条距顶部50px、鼠标不在上方(规定阈值为30以内为上方)、不处于拖动状态时(及mouseup时)停止计时器
if((body_scrillTop < 50) || clientYY > 30 || move == false){
clearInterval(touchRollUp);
roll = false;
return;
}
// 在滚动条移动的同时给移动的元素减小Y值
body_scrillTop = body_scrillTop - 10;
$(window).scrollTop(body_scrillTop);
li_move();
},100);
}
mousedownPosition = event.originalEvent.targetTouches[0].clientY;
});
$('.listItem').bind('touchend',function(event) {
theDiv.insertBefore(tempDiv); // 拖拽元素插入到 虚线div的位置上
theDiv.removeAttr("style");
theDiv.attr("class", "listItem"); //恢复对象的初始样式
theDiv = null; //用完的对象记得释放
tempDiv.remove(); // 删除新建的虚线div
move = false;
touch = false;
$(document.body).css({
"overflow-x":"auto",
"overflow-y":"auto"
});
});
$(".listItem").mousedown(function(event) {
$(document.body).css({
"overflow-x":"hidden",
"overflow-y":"hidden"
});
//拖拽对象
theDiv = $(this);
theDivHeight = theDiv.height();
theDivWidth = theDiv.width();
// theDivPosition = event.clientY - event.offsetY;
theDivPosition = theDiv.offset().top - $(window).scrollTop();
mousedownPosition = event.clientY;
move = true;
theDiv.attr("class", "listItemdash");
theDiv.css({ top: theDivPosition});
lastPos.y = theDivPosition;
tempDiv.insertBefore(theDiv);
$(".listItemdash").css({
"height":theDivHeight,
"width":theDivWidth,
"cursor": "pointer",
"z-index": 99999
});
$(".dash").css("height",theDivHeight);
});
$(".listItem").mousemove(function(event) {
if (!move) return false;
if(touch) return false;
console.log('mousemove');
var body_scrillTop = $(window).scrollTop();//方法返回或设置匹配元素的滚动条的垂直位置。
var scrollHeight = $('body').height();//整体内容的高度
var windowHeight = $(window).height(); //窗口高度
// console.log(theDiv.offset().top);
clientYY = event.clientY;
function liMove(){
lastPos.y = lastPos.y + (clientYY - mousedownPosition);
lastPos.y1 = theDiv.offset().top + theDivHeight;
// 拖拽元素随鼠标移动
theDiv.css({ top: lastPos.y});
// 拖拽元素随鼠标移动 查找插入目标元素
var $listItem = $('.listItem'); // 局部变量:按照重新排列过的顺序 再次获取 各个元素的坐标,
$listItem.each(function() {
tarDiv = $(this);
tarPos.x = tarDiv.offset().left;
tarPos.y = tarDiv.offset().top;
tarPos.y1 = tarPos.y;
// console.log(tarPos.y);
tarFirst = $listItem.eq(0); // 获得第一个元素
tarFirstY = tarFirst.offset().top; // 第一个元素对象的中心纵坐标
//拖拽对象 移动到第一个位置
if (theDiv.offset().top <= tarFirstY) {
tempDiv.insertBefore(tarFirst);
}
//判断要插入目标元素的 坐标后, 直接插入
if (theDiv.offset().top >= tarPos.y - theDivHeight / 2 && lastPos.y1 >= tarPos.y1) {
tempDiv.insertAfter(tarDiv);
}
});
}
liMove();
//下滚——当鼠标在浏览器窗口下方、滚动条没有到达底部、计时器没有在执行时
if(event.clientY > windowHeight-45 && (body_scrillTop+windowHeight < scrollHeight-30) && !roll){
roll = true;
var RollDown = setInterval(function(){
// 在到达底部或鼠标不在底部和不处于拖动状态时停止计时器
if ((body_scrillTop+windowHeight > scrollHeight-30) || (clientYY < windowHeight-45) || move == false){
clearInterval(RollDown);
roll = false;
return;
}
//在滚动条移动的同时给移动的元素增加Y值
body_scrillTop = body_scrillTop + 10;
$(window).scrollTop(body_scrillTop);
liMove();
},100);
}
//上滚——鼠标在浏览器窗口上方、计时器没有执行时
if(event.clientY < 35 && (body_scrillTop > 50) && !roll) {
roll = true;
var RollUp = setInterval(function(){
// 在滚动条距顶部50px、鼠标不在上方(规定阈值为30以内为上方)、不处于拖动状态时(及mouseup时)停止计时器
if((body_scrillTop < 50) || clientYY > 30 || move == false){
clearInterval(RollUp);
roll = false;
return;
}
// 在滚动条移动的同时给移动的元素减小Y值
body_scrillTop = body_scrillTop - 10;
$(window).scrollTop(body_scrillTop);
liMove();
},100);
}
mousedownPosition = event.clientY;
}).mouseup(function(event) {
theDiv.insertBefore(tempDiv); // 拖拽元素插入到 虚线div的位置上
theDiv.removeAttr("style");
theDiv.attr("class", "listItem"); //恢复对象的初始样式
theDiv = null; //用完的对象记得释放
tempDiv.remove(); // 删除新建的虚线div
move = false;
$(document.body).css({
"overflow-x":"auto",
"overflow-y":"auto"
});
});
}
But the problem is iam not able to rotate the sprite image based on user interaction.for eg: when the user moves the mouse on right the frame on right side should moved and when the user moves on left the left side of the frames should me moved iam not able to implement this in fabric js. What i have done is just rotating the sprite image onmouse move.Expected output i want is like this :https://codyhouse.co/demo/360-degrees-product-viewer/index.html
var URL = 'https://codyhouse.co/demo/360-degrees-product-viewer/img/alfa.png';
var canvas = new fabric.Canvas('canvas');
var positions = {
topSteps: 1,
leftSteps: 16
};
var y = 0;
var x = 0;
var topStep;
var leftStep;
canWalk(URL, positions);
function canWalk(URL, positions) {
var myImage = new Image();
myImage.src = URL;
//var mDown = false;
//onloadevent
myImage.onload = function () {
topStep = myImage.naturalHeight / positions.topSteps;
leftStep = myImage.naturalWidth / positions.leftSteps;
var docCanvas = document.getElementById('canvas');
docCanvas.height = topStep;
docCanvas.width = leftStep;
fabricImageFromURL(x, y);
};
}
//mouseevents
canvas.on('mouse:out', function (event) {
console.log("mouseout")
/* x=0;
y=0;
fabricImageFromURL(x,y);*/
});
canvas.on('mouse:move', function (event) {
resetvalue();
setTimeout(function () {
console.log('value of x in start', x)
console.log('positions.leftSteps', positions.leftSteps)
if (x == positions.leftSteps) {
y = 1;
fabricImageFromURL(-y * topStep, -x * leftStep)
}
else {
fabricImageFromURL(-y * topStep, -x * leftStep)
if (x < positions.leftSteps) {
x++;
}
}
}, 50);
});
function resetvalue() {
if (x == positions.leftSteps) {
x = 0;
y = 0;
console.log("x and y value reset to0")
}
}
function fabricImageFromURL(top, left) {
console.log('fabricImageFromURL value', top, left);
fabric.Image.fromURL(URL, function (oImg) {
oImg.set('left', left).set('top', top);
oImg.hasControls = false;
oImg.hasBorders = false;
oImg.selectable = false;
canvas.add(oImg);
canvas.renderAll();
}, { "left": 0, "top": 0, "scaleX": 1, "scaleY": 1 });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.js"></script>
<canvas id="canvas"></canvas>
As we are not changing top value, no need to set top value all the time, just set left value for the image. pixelToSkip is the difference between mousemove pixel to call the function, if mouse move difference is less than that , then it wont call.
var URL = 'https://codyhouse.co/demo/360-degrees-product-viewer/img/alfa.png';
var canvas = new fabric.Canvas('canvas', {
selection: false
}),
imgObject;
var positions = {
topSteps: 1,
leftSteps: 16
};
var y = 0;
var x = 0;
var topStep;
var leftStep;
var isMouseDown = false;
var imgObject, pixelToSkip = 10;
var clickedPointer, currPointer, diff;
canWalk(URL, positions);
function canWalk(URL, positions) {
var myImage = new Image();
myImage.src = URL;
//var mDown = false;
//onloadevent
myImage.onload = function() {
topStep = myImage.naturalHeight / positions.topSteps;
leftStep = myImage.naturalWidth / positions.leftSteps;
canvas.setDimensions({
height : topStep,
width : leftStep
})
fabricImageFromURL(x, y);
};
}
//mouseevents
canvas.on('mouse:down', function(event) {
isMouseDown = true;
prevPointer = canvas.getPointer(event.e);
})
canvas.on('mouse:out', function(event) {
//console.log("mouseout")
/* x=0;
y=0;
fabricImageFromURL(x,y);*/
});
canvas.on('mouse:move', function(event) {
if (!isMouseDown) return;
currPointer = canvas.getPointer(event.e);
diff = currPointer.x - prevPointer.x;
if (diff < -pixelToSkip) {
if (x == positions.leftSteps) {
x = 0;
}
fabricImageFromURL(-x * leftStep)
x++;
prevPointer = currPointer;
} else if(diff > pixelToSkip){
if (x == 0) {
x = positions.leftSteps;
}
x--;
fabricImageFromURL(-x * leftStep)
prevPointer = currPointer;
}
});
canvas.on('mouse:up', function(event) {
isMouseDown = false;
})
function fabricImageFromURL(left) {
if (!imgObject) return
imgObject.set('left', left);
canvas.renderAll();
}
fabric.Image.fromURL(URL, function(oImg) {
imgObject = oImg;
oImg.set({
left: 0,
top: 0,
hasControls: false,
hasBorders: false,
selectable: false
});
canvas.add(oImg);
canvas.renderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.js"></script>
<canvas id="canvas"></canvas>
Content.js
function GetFilterfromStore3(document_root) {
var numberdata = 0;
chrome.storage.sync.get("fiterlink", function (filterlist) {
var links = [];
var number = 0;
node = document_root.getElementsByTagName("a");
//alert(filterlist.fiterlink[0].data.link)
for (var i = 0; i < filterlist.fiterlink.length; i++) {
if (filterlist.fiterlink[i].data.selected == true) {
links[i] = filterlist.fiterlink[i].data.link;
}
}
for (var i = 0; i < node.length; i++) {
for (var j = 0; j < links.length; j++) {
if (node[i].getAttribute("href") != null) {
if (node[i].getAttribute("href").indexOf(links[j]) != -1) {
number++;
}
}
}
}
alert(number + " :1");
numberdata = number;
});
return (numberdata);
};
chrome.runtime.sendMessage({
action: "getnumberoflink",
number: GetFilterfromStore3(document),
});
Event.js
chrome.tabs.onSelectionChanged.addListener(function (activeInfo) {
chrome.tabs.executeScript(null, { file: 'Getnumberoflinks.js' });
// Perform the callback when a message is received from the content script
chrome.runtime.onMessage.addListener(function (message) {
if (message.action == "getnumberoflink") {
chrome.browserAction.setBadgeBackgroundColor({ color: [255, 0, 0, 255] });
chrome.browserAction.setBadgeText({ text: message.number.toString() });
}
});
});
I need to get number from content.js in event.js.
i have some filterlist in my chrome storage and after this i am finding links count on page.
but in event.js i get 0
please help me out and comment if you have some confusion.
I am trying to export rallygrid data in excel file, but getting only headers not values.
Below is my code which I wrote to generate grid and export button
From here [https://github.com/andreano/TaskDelta/blob/master/App.js], I stole the export code
prepareChart: function(iteration_data) {
this.converted_values = [];
this.accept_values = [];
this.commit_values = [];
parents = [];
rootParent = this.getContext().getProject().Name;
sortedArray = [];
var project_hash = {}; // project_by_name, with children
Ext.Array.each(iteration_data, function(iteration){
if ((iteration.ProjectName != rootParent && iteration.ChildCount > 0) || iteration.ParentName == rootParent) {
parents.push(iteration.ProjectName);
}
// make a place for me
if ( ! project_hash[iteration.ProjectName] ) { project_hash[iteration.ProjectName] = []; }
// make a place for my parent so it can know children
if ( iteration.ParentName ) {
if ( ! project_hash[iteration.ParentName]) { project_hash[iteration.ParentName] = []; }
project_hash[iteration.ParentName] = Ext.Array.merge( project_hash[iteration.ParentName], iteration.ProjectName);
}
}, this);
// build order this way:
//console.log("Current: ", this.getContext().getProject().Name );
// order the array by parents to children to grandchildren
sortedArray = this._getTreeArray( this.getContext().getProject().Name , project_hash);
parents = Ext.Array.unique(parents);
sortedData = [];
Ext.Array.each(sortedArray, function(name){
Ext.Array.each(iteration_data, function(ite){
if(ite.ProjectName == name) {
sortedData.push(ite);
};
});
});
Ext.Array.each(iteration_data, function(iteration){
if (iteration.ProjectName == rootParent) {
sortedData.push(iteration);
}
}, this);
iteration_data = sortedData;
sprints = [];
teams = [];
this.ratio = {};
for ( var i=0; i<iteration_data.length; i++ ) {
commit_accept_ratio = 0;
var data_point = iteration_data[i];
this.commit_values.push( data_point.Commit );
this.accept_values.push( data_point.Accept );
if ( data_point.Commit > data_point.Accept ) {
this.converted_values.push( data_point.Commit - data_point.Accept );
} else {
this.converted_values.push( 0 );
}
if (data_point.Commit != 0) {
commit_accept_ratio = (data_point.Accept / data_point.Commit ) * 100;
} else {
commit_accept_ratio = 0;
};
sprints.push(iteration_data[i].Name);
teams.push(iteration_data[i].ProjectName);
teams.push(rootParent);
this.ratio[data_point.ObjectID] = commit_accept_ratio;
}
this.sprints = Ext.Array.unique(sprints).sort();
this.teams = Ext.Array.unique(teams);
removable_teams = [];
for ( var i=0; i<this.teams.length; i++ ) {
team_name = null;
var count = 0;
Ext.Array.each(iteration_data, function(data) {
if (this.teams[i] == data.ProjectName && data.Commit == 0 || null || undefined && data.Accept == 0 || null || undefined) {
count += 1;
team_name = data.ProjectName;
}
}, this);
if (count == this.sprints.length) {
removable_teams.push(team_name);
}
}
removable_teams = Ext.Array.unique(removable_teams);
records = [];
recordHash = {};
summaryHash = {};
Ext.Array.each(iteration_data, function(iter) {
if (!recordHash[iter.ProjectName]) {
recordHash[iter.ProjectName] = {
Team: iter.ProjectName,
Name: '4 Sprint Summary',
Commit: [],
Accept: [],
Perc: [],
Summary: 0
};
}
if (!Ext.Array.contains(removable_teams, iter.ProjectName)) {
recordHash[iter.ProjectName]["Commit-" + iter.Name] = iter.Commit;
recordHash[iter.ProjectName]["Accept-" + iter.Name] = iter.Accept;
recordHash[iter.ProjectName]["Perc-" + iter.Name] = this.ratio[iter.ObjectID];
}
}, this);
var summaryArray = Ext.Array.slice( this.sprints, (this.sprints.length - 4))
var iterated_data = [];
Ext.Array.each(summaryArray, function(summ){
Ext.Array.each(iteration_data, function(team) {
if( summ == team.Name){
iterated_data.push(team);
}
});
});
Ext.Array.each(iteration_data, function(summ){
Ext.Array.each(iterated_data, function(team) {
if (!summaryHash[team.ProjectName]) {
summaryHash[team.ProjectName] = {
Commit: 0,
Accept: 0,
Total: 0
};
};
if (!Ext.Array.contains(removable_teams, team.ProjectName)) {
if( summ.ProjectName == team.ProjectName && summ.Name == team.Name) {
summaryHash[team.ProjectName]["Commit"] += summ.Commit;
summaryHash[team.ProjectName]["Accept"] += summ.Accept;
if (summaryHash[team.ProjectName]["Commit"] != 0) {
summaryHash[team.ProjectName]["Total"] = (summaryHash[team.ProjectName]["Accept"] / summaryHash[team.ProjectName]["Commit"] ) * 100;
} else {
summaryHash[team.ProjectName]["Total"] = 0;
};
};
}
});
}, this);
Ext.Object.each(recordHash, function(key, value) {
if (summaryHash[key]) {
value["Summary"] = summaryHash[key].Total;
records.push(value);
}
});
var cfgsValues = [];
cfgsValues.push({text: 'Team', style:"background-color: #D2EBC8", dataIndex: 'Team', width: 170, renderer: function(value, meta_data, record, row, col) {
if (Ext.Array.contains(parents, value)) {
meta_data.style = "background-color: #FFF09E";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else if (rootParent == value){
meta_data.style = "background-color: #CC6699";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else {
return value;
};
}});
cfgsValues.push({text: '4 Sprint Summary', style:"background-color: #D2EBC8", width: 70, dataIndex: 'Summary', renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
};
meta_data.style = "background-color: "+color+"";
return Ext.Number.toFixed(value, 0)+"%";
}});
Ext.Array.each(this.sprints, function(sprint) {
cfgsValues.push(
{text: sprint, style:'background-color:#D2EBC8;text-align:center;font-weight:bold', defaults: {enableColumnHide:false}, columns:[
{text: "Commit", dataIndex: 'Commit-' + sprint, width: 50, renderer: function(value, meta_data, record) {
if( value ) {
return value;
} else {
return "NA";
}
}},
{text: "Accept", dataIndex: 'Accept-' + sprint, width: 60, renderer: function(value, meta_data, record) {
if( value) {
return value;
} else {
return "NA";
}
}},
{text: "%", dataIndex: 'Perc-'+ sprint, width: 50, renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
}
meta_data.style = "background-color: "+color+"";
if (value) {
return Ext.Number.toFixed(value, 0)+"%";
} else {
return "NA";
};
}}
]}
);
});
var chart = Ext.getCmp('mychart');
if (chart) {
chart.destroy();
};
Ext.Array.each(this.sprints, function(sprint) {
Ext.Array.each(records, function(record) {
if (record["Accept-" + sprint] == undefined) {
record["Accept-" + sprint] = undefined;
}
if (record["Commit-" + sprint] == undefined) {
record["Commit-" + sprint] = undefined;
}
if (record["Perc-" + sprint] == undefined) {
record["Perc-" + sprint] = undefined;
}
});
});
this.add({
xtype: 'rallygrid',
id: 'mychart',
store: Ext.create('Rally.data.custom.Store', {
data: records,
pageSize: 100
}),
//viewConfig: {
//stripeRows: false
//},
columnCfgs: cfgsValues,
//columnLines: true
});
this.globalStore = Ext.getCmp('mychart');
console.log("this.globalStore", this.globalStore);
this.down('#grid_box').add(this.globalStore);
//this.setLoading(false);
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'Export to Excel',
disabled: false,
margin: '20 10 10 0',
region: "right",
handler: function() {
me._onClickExport();
}
});
},
_onClickExport: function () { //using this function to export to csv
var that = this;
if (this.down('#grid_box')){
//Ext.getBody().mask('Exporting Tasks...');
//console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = that.getComponent('grid_box');
//console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
//console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
}
There is an example in new AppSDK2 documentation of exporting to CSV.
I also have an example of exporting to CSV in this github repo.