I have modified some code of Fabric.text to get arc shape of text . But the problem is toSVG() is rendering the plain text instead of arc text. How can i render it properly.
https://i.stack.imgur.com/A3bnx.png (Rendering Canvas)
https://i.stack.imgur.com/R4cY0.png (Exported SVG)
If there is an alternative way to print in svg then let me know.
_renderText: function(ctx) {
this._translateForTextAlign(ctx);
/* this._renderTextFill(ctx);*/
this._getCircularText(ctx)
/* this._renderTextStroke(ctx);*/
this._translateForTextAlign(ctx, true);
},
_getCircularText: function(ctx)
{
var text = this.text,
arc = this.arc/4,
inwardFacing = true,
angle = 0,
kerning = 0,
cw=0,
x,
clockwise = 1;
if(arc>0){
clockwise=1;
inwardFacing=true;
}else{
clockwise=-1;
inwardFacing=false;
}
var lineHeights = 0;
var widthOftext=0;
var maxHeight=0;
;
for (x = 0; x < text.length; x++) {
cw = ctx.measureText(text[x]).width;
angle=(arc)/((text.length*cw));
if(arc){
ctx.rotate(-angle/2);
}
}
for (x = 0; x < text.length; x++) {
cw = ctx.measureText(text[x]).width;
var heightOfLine = this._getHeightOfLine(ctx, x, text);
angle=(arc)/((text.length*cw));
ctx.rotate(angle);
lineHeights += heightOfLine;
ctx.fillText(text[x], (-this.width/2)+widthOftext , (-this.height/2)+heightOfLine);
widthOftext+= cw;
}
},
You can find the code for SVG Export below
toSVG: function(reviver) {
// console.log(20833);
var markup = this._createBaseSVGMarkup(),
offsets = this._getSVGLeftTopOffsets(this.ctx),
textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft);
console.log(textAndBg);
this._wrapSVGTextAndBg(markup, textAndBg);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/**
* #private
*/
_getSVGLeftTopOffsets: function(ctx) {
var lineTop = this._getHeightOfLine(ctx, 0),
textLeft = -this.width / 2,
textTop = 0;
return {
textLeft: textLeft + (this.group && this.group.type === 'path-group' ? this.left : 0),
textTop: textTop + (this.group && this.group.type === 'path-group' ? -this.top : 0),
lineTop: lineTop
};
},
/**
* #private
*/
_wrapSVGTextAndBg: function(markup, textAndBg) {
var noShadow = true, filter = this.getSvgFilter(),
style = filter === '' ? '' : ' style="' + filter + '"';
console.log(textAndBg.textSpans);
markup.push(
'\t<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
style, '>\n',
textAndBg.textBgRects.join(''),
'\t\t<text ',
(this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ': ''),
(this.fontSize ? 'font-size="' + this.fontSize + '" ': ''),
(this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
(this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
(this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
'style="', this.getSvgStyles(noShadow), '" >\n',
textAndBg.textSpans.join(''),
'\t\t</text>\n',
'\t</g>\n'
);
},
/**
* #private
* #param {Number} textTopOffset Text top offset
* #param {Number} textLeftOffset Text left offset
* #return {Object}
*/
_getSVGTextAndBg: function(textTopOffset, textLeftOffset) {
var textSpans = [ ],
textBgRects = [ ],
height = 0;
// bounding-box background
this._setSVGBg(textBgRects);
// console.log(this._textLines);
// text and text-background
for (var i = 0, len = this._textLines.length; i < len; i++) {
if (this.textBackgroundColor) {
this._setSVGTextLineBg(textBgRects, i, textLeftOffset, textTopOffset, height);
}
this._setSVGTextLineText(i, textSpans, height, textLeftOffset, textTopOffset, textBgRects);
height += this._getHeightOfLine(this.ctx, i);
}
return {
textSpans: textSpans,
textBgRects: textBgRects
};
},
_setSVGTextLineText: function(i, textSpans, height, textLeftOffset, textTopOffset) {
var yPos = this.fontSize * (this._fontSizeMult - this._fontSizeFraction)
- textTopOffset + height - this.height / 2;
if (this.textAlign === 'justify') {
// i call from here to do not intefere with IText
this._setSVGTextLineJustifed(i, textSpans, yPos, textLeftOffset);
return;
}
// console.log(textSpans);
textSpans.push(
'\t\t\t<tspan x="',
toFixed(textLeftOffset + this._getLineLeftOffset(this._getLineWidth(this.ctx, i)), NUM_FRACTION_DIGITS), '" ',
'y="',
toFixed(yPos, NUM_FRACTION_DIGITS),
'" ',
// doing this on <tspan> elements since setting opacity
// on containing <text> one doesn't work in Illustrator
this._getFillAttributes(this.fill), '>',
fabric.util.string.escapeXml(this._textLines[i]),
'</tspan>\n'
);
// console.log(textSpans);
},
_setSVGTextLineJustifed: function(i, textSpans, yPos, textLeftOffset) {
var ctx = fabric.util.createCanvasElement().getContext('2d');
this._setTextStyles(ctx);
var line = this._textLines[i],
words = line.split(/\s+/),
wordsWidth = this._getWidthOfWords(ctx, line),
widthDiff = this.width - wordsWidth,
numSpaces = words.length - 1,
spaceWidth = numSpaces > 0 ? widthDiff / numSpaces : 0,
word, attributes = this._getFillAttributes(this.fill),
len;
textLeftOffset += this._getLineLeftOffset(this._getLineWidth(ctx, i));
for (i = 0, len = words.length; i < len; i++) {
word = words[i];
textSpans.push(
'\t\t\t<tspan x="',
toFixed(textLeftOffset, NUM_FRACTION_DIGITS), '" ',
'y="',
toFixed(yPos, NUM_FRACTION_DIGITS),
'" ',
// doing this on <tspan> elements since setting opacity
// on containing <text> one doesn't work in Illustrator
attributes, '>',
fabric.util.string.escapeXml(word),
'</tspan>\n'
);
textLeftOffset += this._getWidthOfWords(ctx, word) + spaceWidth;
}
},
_setSVGTextLineBg: function(textBgRects, i, textLeftOffset, textTopOffset, height) {
textBgRects.push(
'\t\t<rect ',
this._getFillAttributes(this.textBackgroundColor),
' x="',
toFixed(textLeftOffset + this._getLineLeftOffset(this._getLineWidth(this.ctx, i)), NUM_FRACTION_DIGITS),
'" y="',
toFixed(height - this.height / 2, NUM_FRACTION_DIGITS),
'" width="',
toFixed(this._getLineWidth(this.ctx, i), NUM_FRACTION_DIGITS),
'" height="',
toFixed(this._getHeightOfLine(this.ctx, i) / this.lineHeight, NUM_FRACTION_DIGITS),
'"></rect>\n');
},
_setSVGBg: function(textBgRects) {
if (this.backgroundColor) {
textBgRects.push(
'\t\t<rect ',
this._getFillAttributes(this.backgroundColor),
' x="',
toFixed(-this.width / 2, NUM_FRACTION_DIGITS),
'" y="',
toFixed(-this.height / 2, NUM_FRACTION_DIGITS),
'" width="',
toFixed(this.width, NUM_FRACTION_DIGITS),
'" height="',
toFixed(this.height, NUM_FRACTION_DIGITS),
'"></rect>\n');
}
},
/**
* Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
* we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
*
* #private
* #param {Any} value
* #return {String}
*/
_getFillAttributes: function(value) {
var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
return 'fill="' + value + '"';
}
return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
},
I want to solve Bliffoscope Data Analysis Problem using javascript. I have following question.
This is SlimeTorpedo
+
+
+++
+++++++
++ ++
++ + ++
++ +++ ++
++ + ++
++ ++
+++++++
+++
This is TestData
+ + + ++ + +++ + +
+ ++ + + ++++ + + + + + + +++ +++ +
+ + + ++ ++ ++ + ++ + + + + +
+ ++ + ++ + + + ++ ++ + +
++++++ + + + ++ + + + + ++ + + +
+ + + + + ++ + ++ + + + +
+++ + ++ + + + +++ + + ++ +
+++++ + + + + + + + +
+ + + + + + + + + + + +
++ + + + ++ + + + ++
There is one question already similar to this but in Java. This question is asked here.
How can I solve this in JavaScript.
UPDATE
I tried following solution.
const fs = require('fs');
let torpedo = [], starship = [], testData = [];
// counter = -1;
function getTorpedoData(fileName, type) {
let counter = -1;
return new Promise(function(resolve, reject) {
fs.readFile(fileName,'utf8', (err, data) => {
if (err) {
reject();
} else {
for (let i = 0; i < data.length; i++) {
if (data[i] == '\n' || counter === -1) {
torpedo.push([]);
counter++;
} else {
torpedo[counter].push(data[i]);
}
}
}
console.log(data);
resolve();
});
});
}
function getTestData(fileName, type) {
let counter = -1;
return new Promise(function(resolve, reject) {
fs.readFile(fileName,'utf8', (err, data) => {
if (err) {
reject();
} else {
for (let i = 0; i < data.length; i++) {
if (data[i] == '\n' || counter === -1) {
testData.push([]);
counter++;
} else {
testData[counter].push(data[i]);
}
}
}
console.log(data);
resolve();
});
});
}
let score = 0;
getTorpedoData('./SlimeTorpedo.blf', 'torpedo').then((data) => {
getTestData('./TestData.blf', 'testData').then(() => {
torpedo.forEach((torpedoArray, torpedoIndex) => {
torpedoArray.filter((contents) => {
if (contents === '+') {
testData.forEach((testDataArray) => {
testDataArray.filter((dataContents, dataIndex) => {
// console.log(dataContents);
if (dataContents === '+') {
if (torpedoIndex === dataIndex) {
score++;
}
// console.log(score);
}
});
});
}
});
});
});
});
I creating 3 arrays torpedo, starship and testData. I read all these files and put them in multidimensional array(above). Then I am trying to find compare the indexes if torpedo array in testData array. However, there is something I am doing wrong. How can I fix it?
[Edit by Spektre]
Test results for test data (both this and the one from #greybeard link):
Red mean mismatch and Yellow mean match. Score is incremented for match and decremented for mismatch. x counts from zero to rightwards and y counts from zero downwards but your data was enlarged by empty line so you can count from 1 instead ...
Are you looking for something like this (Fiddle)?
// Create our images: torpedo (object) and background (context)
var object = " +\n +\n +++\n +++++++\n ++ ++\n++ + ++\n++ +++ ++\n++ + ++\n ++ ++\n +++++++\n +++",
context = " + + + ++ + +++ + +\n + ++ + + ++++ + + + + + + +++ +++ +\n + + + ++ ++ ++ + ++ + + + + +\n+ ++ + ++ + + + ++ ++ + +\n ++++++ + + + ++ + + + + ++ + + +\n + + + + + ++ + ++ + + + +\n+++ + ++ + + + +++ + + ++ +\n +++++ + + + + + + + +\n + + + + + + + + + + + +\n ++ + + + ++ + + + ++ ";
var c = document.getElementById("test_canvas"),
ctx = c.getContext("2d"),
scale = 10;
// Draw a pixel on canvas
function draw_pixel(x, y, fill_style) {
ctx.fillStyle = fill_style;
ctx.fillRect(x * scale, y * scale, scale, scale);
}
// Receive an array of coordinates, draw pixels
function draw_image(serialized_image, fill_style) {
for (var i = 0, len = serialized_image.length; i < len; i++) {
draw_pixel(serialized_image[i][0], serialized_image[i][1], fill_style);
}
}
// Receive a text string, turn it into an array of coordinates of filled in pixels
function serialize_map(char_map) {
var x = 0,
y = 0,
c,
map = [];
for (var i = 0, len = char_map.length; i < len; i++) {
c = char_map[i];
if (c == '+') {
map.push([x, y])
}
x += 1;
if (c == '\n') {
x = 0;
y += 1;
}
}
return map;
}
// Find number of intersections between two images
function array_intersect() {
var a, d, b, e, h = [],
f = {},
g;
g = arguments.length - 1;
b = arguments[0].length;
for (a = d = 0; a <= g; a++) {
e = arguments[a].length, e < b && (d = a, b = e);
}
for (a = 0; a <= g; a++) {
e = a === d ? 0 : a || d;
b = arguments[e].length;
for (var l = 0; l < b; l++) {
var k = arguments[e][l];
f[k] === a - 1 ? a === g ? (h.push(k), f[k] = 0) : f[k] = a : 0 === a && (f[k] = 0);
}
}
return h;
}
// Translate the coordinates of a serialized image
function translate(coords, ix, iy) {
return [coords[0] + ix, coords[1] + iy];
}
// Find in which position the object has more intersections with the background
function get_best_position(context, object) {
// Calculate image dimensions
context_width = context.sort(function(a, b) {
return b[0] - a[0];
})[0][0];
context_height = context.sort(function(a, b) {
return b[1] - a[1];
})[0][1];
object_width = object.sort(function(a, b) {
return b[0] - a[0];
})[0][0];
object_height = object.sort(function(a, b) {
return b[1] - a[1];
})[0][1];
// Swipe context, store amount of matches for each patch position
similaritudes = [];
for (var cx = 0; cx < context_width; cx++) {
for (var cy = 0; cy < context_height; cy++) {
translated_object = object.map(function(coords) {
return translate(coords, cx, cy);
})
intersection = array_intersect(context, translated_object);
// console.log(translated_object);
similaritudes[intersection.length] = [cx, cy];
}
}
// Return position for which number of matches was greater
return similaritudes.slice(-1)[0];
}
// Parse our images from the text strings
var serialized_context = serialize_map(context);
var serialized_object = serialize_map(object);
// Find best position for our torpedo
var best_position = get_best_position(serialized_context, serialized_object);
// Translate torpedo to best position
positioned_object = serialized_object.map(function(coords) {
return translate(coords, best_position[0], best_position[1]);
});
// Draw background and torpedo
draw_image(serialized_context, "gray");
draw_image(positioned_object, "rgba(0, 255, 0, 0.5)");
<canvas id="test_canvas" width="800" height="120" style="border:1px solid #000000;">
</canvas>
I have a game board 8x8. below is the code to create the board...
for (var i = 0 ; i < TOTAL_ROWS ; i++) {
tab_imgs[i] = [];
for (var j = 0 ; j < TOTAL_COLUMNS ; j++) {
var num_img = Math.ceil(Math.random() * NUM_IMGS);
if (i > 1) {
while(tab_imgs[i-2][j] == num_img && tab_imgs[i-1][j] == num_img){
num_img = Math.ceil(Math.random() * NUM_IMGS);
}
}
if (j > 1) {
while(tab_imgs[i][j-2] == num_img && tab_imgs[i][j-1] == num_img){
num_img = Math.ceil(Math.random() * NUM_IMGS);
if (i > 1) {
while(tab_imgs[i-2][j] == num_img && tab_imgs[i-1][j] == num_img){
num_img = Math.ceil(Math.random() * NUM_IMGS);
}
}
}
}
tab_imgs[i][j] = num_img;
render_table += '<div class="jewel jewel_' + num_img + '" data-row="' + i + '" data-col="' + j + '" data-jewel="' + num_img + '" style="top: ' + Number(i*TOTAL_IMGS) + 'px; left: ' + Number(j*TOTAL_IMGS) + 'px;"></div>';
}
}
The code above will produce a random board. My issue is when the user resumes an existing game. I need to loop through the ALREADY produced html and grab the attribute "data-jewel" so instead of these lines:
num_img = Math.ceil(Math.random() * NUM_IMGS);
I have:
num_img = attribute('data-jewel')
for each grid;
You can retrieve the value of a data attribute by using the jQuery .data() method:
num_img = +$('.selector').data( 'jewel' );
I have implemented the Game of Life in Javascript. I am thinking about making a combobox which a listof colors to choose from. From there I want to render the obj.innerHTML img source of my grid cells in the game. What would be the best way to go about this?
Here is my code so far for the game:
<br>
<canvas id="canvas" width="100%" height="80%"></canvas>
<button onClick="generateLevel()">Create New Board</button>
<button onClick="step()">Step</button>
<button onClick="multiStep()">Multistep</button>
<script type="text/javascript">
var level;
var lastLevelDrawn;
var lvlWidth = 0;
var lvlHeight = 0;
function step()
{
var tempLevel = new Array(lvlHeight);
for (var y = 0; y < lvlHeight; y++)
{
tempLevel[y] = new Array(lvlWidth);
for (var x = 0; x < lvlWidth; x++)
{
var liveNeighborCount = 0;
var status = level[y][x];
for (var k = y-1; k < y+2; k++)
{
for (var j = x-1; j < x+2; j++)
{
if (k == y && j == x)
{
}
else if (k >= 0 && k < lvlHeight && j >= 0 && j < lvlWidth)
{
if (level[k][j] == "1")
{
liveNeighborCount++;
}
}
}
}
if (level[y][x] == "1")
{
if (liveNeighborCount != 2 && liveNeighborCount != 3)
{
status = "0";
}
}
if (level[y][x] == "0")
{
if (liveNeighborCount == 3)
{
status = "1";
}
}
tempLevel[y][x] = status;
}
}
level = tempLevel;
render();
}
function multiStep()
{
var steps = prompt("How many steps do you want to step?", "10");
for (var x = 0; x < steps; x++)
step();
}
function generateLevel()
{
var width = prompt("How many cells wide?", "10");
var height = prompt("How many cells high?", "10");
lvlWidth = width;
lvlHeight = height;
var output = "";
if (width != null && height != null)
{
level = new Array(lvlHeight);
lastLevelDrawn = new Array(lvlHeight);
for (var y = 0; y < height; y++)
{
level[y] = new Array(lvlWidth);
lastLevelDrawn[y] = new Array(lvlWidth);
for (var x = 0; x < width; x++)
{
level[y][x] = Math.floor((Math.random()*2));
lastLevelDrawn[y][x] = -1;
output += " " + level[y][x];
}
output += "<br>";
}
}
//document.getElementById("demo").innerHTML=output;
setupRender();
}
function changeTile(tile)
{
var x = tile % lvlWidth;
var y = parseInt(tile/lvlWidth);
//document.getElementById("demo").innerHTML=x + ":" + y;
if (level[y][x] == "1")
level[y][x] = "0";
else
level[y][x] = "1";
render();
}
function render()
{
var widPer = ((1/lvlWidth)*90);
var heiPer = ((1/lvlHeight)*70);
for (var y = 0; y < lvlHeight; y++)
{
for (var z = 0; z < lvlWidth; z++)
{
var send = y*lvlWidth + z;
if (lastLevelDrawn[y][z] != level[y][z])
{
lastLevelDrawn[y][z] = level[y][z];
var obj = document.getElementById(send);
if (level[y][z] == "1")
obj.innerHTML = "<img src='white.jpg' width='" + widPer + "%' height='" + heiPer + "%' onclick='changeTile(" + send + ")'/>";
else
obj.innerHTML = "<img src='black.jpg' width='" + widPer + "%' height='" + heiPer + "%' onclick='changeTile(" + send + ")'/>";
}
}
}
}
function setupRender()
{
var x = "";
var widPer = ((1/lvlWidth)*90);
var heiPer = ((1/lvlHeight)*70);
//x += "<table border='1' col width='((1/lvlWidth)*100)%' col height='((1/lvlHeight)*80)%'>";
for (var y = 0; y < lvlHeight; y++)
{
//x += "<tr>";
for (var z = 0; z < lvlWidth; z++)
{
lastLevelDrawn[y][z] = level[y][z];
//x += level[y][z];
var send = y*lvlWidth + z;
x += "<n id='" + send + "'>";
if (level[y][z] == "1")
x += "<img src='white.jpg' width='" + widPer + "%' height='" + heiPer + "%' onclick='changeTile(" + send + ")'/>";
else
x += "<img src='black.jpg' width='" + widPer + "%' height='" + heiPer + "%' onclick='changeTile(" + send + ")'/>";
x += "</n>";
}
x += "<br>";
//x += "</tr>";
}
//x += "</table>";
document.getElementById("demo").innerHTML=x;
}
</script>
I have a function that takes about 10 seconds to complete and it breaks if reactivated before the first iteration is completed - It acts as a text-fader - JQuery is not an option I want to use pure JS
My function:
//Text fading effect
function fadeText() {
var hex1 = 153,
hex2 = 204,
hex3 = 51;
function innerFade() {
if (hex1 >= 30) {
hex1 -= 1;
document.getElementById('confirmation').style.color = "rgb(" + hex1 + "," + hex2 + "," + hex3 + ")";
}
if (hex2 >= 30) {
hex2 -= 1;
document.getElementById('confirmation').style.color = "rgb(" + hex1 + "," + hex2 + "," + hex3 + ")";
}
if (hex3 >= 30) {
hex3 -= 1;
document.getElementById('confirmation').style.color = "rgb(" + hex1 + "," + hex2 + "," + hex3 + ")";
}
setTimeout(innerFade, 20);
}
innerFade();
}
This is activated on button press, I want the first iteration to cancel or set the innerHTML of the div the text has been assigned to to innerHTML=""; if it is activated again before the animation is completed.
Big Edit:
//Set colour function
function setColor(elementName, r, g, b) {
document.getElementById(elementName).style.color = "rgb(" + r + "," + g + "," + b + ")";
}
//Text fading effect
var animationActive = false;
function fadeText() {
if (animationActive) return;
animationActive = true;
var hex1 = 153,
hex2 = 204,
hex3 = 51;
function innerFade() {
var rDone, gDone, bDone;
if (hex1 >= 30) {
hex1 -= 1;
setColor('confirmation', hex1, hex2, hex3);
} else {rDone = true;}
if (hex2 >= 30) {
hex2 -= 1;
setColor('confirmation', hex1, hex2, hex3);
} else {gDone = true;}
if (hex3 >= 30) {
hex3 -= 1;
setColor('confirmation', hex1, hex2, hex3);
} else {bDone = true;}
if (rDone && gDone && bDone) {
animationActive = false;
}
}
setTimeout(innerFade, 20);
}
You could disable the button to prevent calling the function again:
HTML:
<button onclick="fadeText(this)"> Fade text </button>
JavaScript:
function fadeText(btn) {
var elm = document.getElementById('confirmation'),
c = [153, 204, 51];
function loop() {
for (var i = 0; i < c.length; i++) {
if (c[i] >= 30) c[i] -= 1;
}
elm.style.color = 'rgb(' + c[0] + ',' + c[1] + ',' + c[2] + ')';
return c[0] < 30 && c[1] < 30 && c[2] < 30 ?
btn.disabled = false : setTimeout(loop, 20);
}
btn.disabled = true;
loop();
}
Live demo: http://jsfiddle.net/nKV7A/