Построчное заполнение текста цветом фона в Fabric JS / Canvas - PullRequest
0 голосов
/ 20 октября 2018

Я использую библиотеку FabricJS для наложения текста на холст.Мне нужно добавить отступы (в идеале просто влево и вправо) к текстовому элементу, который включает свойство textBackgroundColor.

Вот что я пробовал до сих пор:

let textObject = new fabric.Text('Black & White',{
    fontFamily: this.theme.font,
    fontSize: this.theme.size,
    textBaseline: 'bottom',
    textBackgroundColor: '#000000',
    left: 0,
    top: 0,
    width: 100,
    height: 40,
    padding: 20,
    fill: 'white',
});

Заполнение нене так, как я ожидал.Я попытался использовать свойство backgroundColor, но это добавляет фон ко всему блоку группы, а не только к тексту.

Я мог бы добавить неразрывный пробел для достижения того же эффекта, но это не похоже нанадежное решение, и я надеялся, что Fabric JS разрешит это из коробки.Любые идеи, как этого добиться?

Требуемое решение (вариант справа, с дополнительными отступами - это то, что я хотел бы):

enter image description here

1 Ответ

0 голосов
/ 22 октября 2018

Я даю вам 2 ответа для двух тонко разных случаев.

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

КСТАТИ встроенный атрибут padding для текстовых площадок относительно управляющей границы, но заливка цветом фона не покрывает созданное пустое пространство.Другими словами, он работает как CSS-поле, а не как заполнение CSS.

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

Sample for case 1

Пример фрагмента ниже.

var canvas = window._canvas = new fabric.Canvas('c');

// function to do the drawing. Could easily be accomodated into a class (excluding the canvas reset!) 
function reset(pos)
{

canvas.clear();

// Create the text node - note the position is (0, 0)
var text = new fabric.Text(pos.text, {
    fontFamily: 'Arial',
    left: 0,
    top: 0,
    fill: "#ffffff",
    stroke: "",
    textBackgroundColor: '#000000'
});

// create the outer 'margin' rect, note the position is negatively offset for padding & margin 
// and the width is sized from the dimensions of the text node plus 2 x (padding + margin).
var rectMargin =   new fabric.Rect({
    left: -1 *  (pos.padding.left + pos.margin.left), 
    top: -1 * (pos.padding.top + pos.margin.top),
    width: text.width + ((pos.padding.left + pos.padding.right) + (pos.margin.left + pos.margin.right)), 
    height: text.height + ((pos.padding.top + pos.padding.bottom) + (pos.margin.top + pos.margin.bottom)),
    strokeWidth: pos.border,
    stroke: 'red',
    fill: 'transparent'
})

// create the inner 'padding' rect, note the position is offset for padding only
// and the width is sized from the dimensions of the text node plus 2 x padding.
var rectPadding =   new fabric.Rect({
    width: text.width + (pos.padding.left + pos.padding.right), 
    height: text.height + (pos.padding.top + pos.padding.bottom),
    left: -1 *  pos.padding.left, top: -1 * pos.padding.top,
    fill: 'gold'
})

// create group and add shapes to group, rect first so it is below text.
// note that as the group is oversized, we position it at pos - padding. 
var group = new fabric.Group([ rectMargin, rectPadding, text ], {
  left: pos.x - (pos.padding.left - pos.margin.left),
  top: pos.y - (pos.padding.top - pos.margin.top),
  angle: pos.angle,
});

canvas.add(group);
}

// function to grab values from user inputs
function go()
{
var m = $('#margin').val().split(',');
var p = $('#padding').val().split(',');
for (var i = 0 ; i < 4; i = i + 1)
{
  p[i] = parseInt(p[i], 10); // ensure we have numbers and not strings !
  m[i] = parseInt(m[i], 10);
}


// Object holding position and content info
var pos = {x: 50, y : 10, text: 'Text with padding\nand another line', 
  padding: {top:p[0], right:p[1], bottom: p[2], left: p[3]}, margin: {top:m[0], right:m[1], bottom: m[2], left: m[3]}, border: 1, angle: 10};

reset(pos);
}

// click handler for go button
$('#go').on('click', function(e){
  go();
  
})

// call go once to show on load
go();
div
{
  background-color: silver;
  width: 600px;
  height: 300px;
}
.ipt
{
margin-right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<p>
<span class='ipt'> Margin: <input id='margin' value = '12,10,12,10' /></span> 
<span class='ipt'> Padding: <input id='padding' value = '0,5,0,5' /></span> 
<span class='ipt'><button id='go' />Go</button></span> 
<div>
  <canvas id="c" width="600" height="300"></canvas>
</div>

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

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

Sample for case 2.

var textIn = 'Text goat\nMillenium jam\nplumb\nBlack & White'

var canvas = window._canvas = new fabric.Canvas('c');

// function to do the drawing. Could easily be accomodated into a class (excluding the canvas reset!) 
function reset(pos)
{

 canvas.clear();
  
// Create the text measuring node - not added to the canvas !
var textMeasure = new fabric.IText(pos.text, {
    fontFamily: 'Arial',
    left: 0,
    top: 0,
    fill: "#ffffff",
    stroke: "",
    textBackgroundColor: '#000000'
});

// loop round the lines in the text creating a margin/pad scenario for each line
var theText, text, textHeight, rectPadding, rectMargin, top = 0, shapes = [];
for (var i = 0; i < textMeasure._textLines.length; i = i + 1){
  theText = textMeasure._textLines[i].join('');
  textHeight = Math.floor(textMeasure.lineHeight * textMeasure.fontSize) //textMeasure.getHeightOfLine(i)

  // Make the text node for line i
  text = new fabric.IText(theText, {
      fontFamily: 'Arial',
      left: 0,
      top: top,
      fill: "#ffffff",
      stroke: ""
  });


  // create the outer 'margin' rect, note the position is negatively offset for padding & margin 
  // and the width is sized from the dimensions of the text node plus 2 x (padding + margin).
  rectMargin =   new fabric.Rect({
      left: -1 *  (pos.padding.left + pos.margin.left), 
      top: top - (pos.padding.top + pos.margin.top),
      width: text.width + ((pos.padding.left + pos.padding.right) + (pos.margin.left + pos.margin.right)), 
      height: textHeight + ((pos.padding.top + pos.padding.bottom) + (pos.margin.top + pos.margin.bottom)),
      fill: 'transparent'
  })
  shapes.push(rectMargin);

  // create the inner 'padding' rect, note the position is offset for padding only
  // and the width is sized from the dimensions of the text node plus 2 x padding.
  rectPadding =   new fabric.Rect({
      width: text.width + (pos.padding.left + pos.padding.right), 
      height: textHeight + (pos.padding.top + pos.padding.bottom),
      left: -1 *  pos.padding.left, 
      top: top - pos.padding.top,
      fill: '#000000ff'
  })
  shapes.push(rectPadding);
  shapes.push(text);

  // move the insert point down by the height of the line
  var gap = 0; // text.lineHeight - textHeight;
  top = top - 1 + textHeight + pos.padding.top + pos.margin.top + pos.padding.bottom + pos.margin.bottom;
}

// At this point we have a list of shapes to output in the shapes[] array.
// Create group and add the shapes to group.
// note that group is positioned so that the topleft of the first text line is where
// it would fall if it were a standard text node. 
var group = new fabric.Group(shapes, {
  left: pos.x - (pos.padding.left - pos.margin.left),
  top: pos.y - (pos.padding.top - pos.margin.top),
  angle: pos.angle,
});

canvas.add(group);

}

// function to grab values from user inputs
function go()
{
var m = $('#margin').val().split(',');
var p = $('#padding').val().split(',');
for (var i = 0 ; i < 4; i = i + 1)
{
  p[i] = parseInt(p[i], 10); // ensure we have numbers and not strings !
  m[i] = parseInt(m[i], 10);
}


// Object holding position and content info
var pos = {x: 70, y : 10, text: textIn, 
  padding: {top:p[0], right:p[1], bottom: p[2], left: p[3]}, margin: {top:m[0], right:m[1], bottom: m[2], left: m[3]}, border: 1, angle: 10};

reset(pos);
}

// click handler for go button
$('#go').on('click', function(e){
  go();
  
})

// call go once to show on load
go();
div
{
  background-color: silver;
  width: 600px;
  height: 100px;
}
.ipt
{
margin-right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<p>
<span class='ipt'> Margin: <input id='margin' value = '0,0,0,0' /></span> 
<span class='ipt'> Padding: <input id='padding' value = '5,15,5,15' /></span> 
<span class='ipt'><button id='go' />Go</button></span> 
<div>
  <canvas id="c" width="600" height="300"></canvas>
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...