Saya mencoba menampilkan informasi textarea yang telah disimpan di MariaDB. Saya tidak punya masalah menyimpan informasi teks. Masalah yang saya hadapi adalah transisi pemformatan dari area teks ke kanvas tempat saya ingin menampilkannya.

Tujuannya adalah agar pengguna mengisi catatan di area teks dan kemudian menampilkannya di laporan kanvas terpisah.

Saat ini, saya bisa membuat wordwrap bekerja dengan sukses menggunakan kode ini yang saya simpan di file wordWrap.js:

function wrapText (c, text, x, y, maxWidth, lineHeight) {

    var words = text.split(' ');
    var line = '';
    var lineCount = 0;
    var test;
    var metrics;

    for (var i = 0; i < words.length; i++) {
        test = words[i];
// add test for length of text
        metrics = c.measureText(test);
        while (metrics.width > maxWidth) {
            test = test.substring(0, test.length - 1);
            metrics = c.measureText(test);
        }

        if (words[i] != test) {
            words.splice(i + 1, 0,  words[i].substr(test.length))
            words[i] = test;
        }  

        test = line + words[i] + ' ';  
        metrics = c.measureText(test);

        if (metrics.width > maxWidth && i > 0) {
            c.fillText(line, x, y);
            line = words[i] + ' ';
            y += lineHeight;
            lineCount++;
        }
        else {
            line = test;
        }
    }

    c.fillText(line, x, y);
}

Saya dapat menambahkan teks, yang membungkus kata berdasarkan ukuran area fillText dan panjang kata. Yang perlu saya tambahkan adalah kemampuan untuk mendukung pengembalian kereta. Pengguna tidak akan kesulitan menggunakan \n untuk mendukung pengembalian kereta jadi saya hanya perlu membuatnya berfungsi.

Saya telah melihat kode lain di luar sana yang mendukung pengembalian kereta. Contoh yang saya mainkan di bawah ini.

ctx.font = '12px Courier';
var text = <?php echo json_encode($row['notes']);?>;
var x = 30;
var y = 30;
var lineheight = 15;
var lines = text.split('\n');

for (var i = 0; i<lines.length; i++) {
    ctx.fillText(lines[i], x, y + (i*lineheight) );
}

Metode-metode ini memiliki atribut yang serupa dan saya yakin mereka dapat disejajarkan tetapi saya mengalami kesulitan mencari tahu bagaimana menerapkan bagian kunci dari kedua skrip yang mendorong pemisahan teks ...

Text.split('\n')

Text.split(' ')

Bagi saya ini terlihat seperti kombinasi for dan while loop seperti yang digunakan Word wrap, tetapi saya perlu bantuan untuk mencari tahu di mana.

0
airider74 8 Agustus 2019, 03:15

1 menjawab

Jawaban Terbaik

Yang terbaik dalam merender teks di browser adalah HTML dan CSS.
Canvas 2D API masih jauh di bawah, jadi ketika Anda perlu merender teks kompleks di atas kanvas, yang terbaik adalah menggunakan kekuatan HTML dan CSS untuk mengambil semua tindakan yang diperlukan untuk kanvas Anda.

Saya sudah membuat beberapa jawaban yang menangani masalah serupa, jadi yang ini hanya adaptasi dari kode-kode sebelumnya dengan kebutuhan Anda:

// see https://stackoverflow.com/questions/55604798
// added x output
function getLineBreaks(node, contTop = 0, contLeft = 0) {
  if(!node) return [];
  const range = document.createRange();
  const lines = [];
  range.setStart(node, 0);
  let prevBottom = range.getBoundingClientRect().bottom;
  let str = node.textContent;
  let current = 1;
  let lastFound = 0;
  let bottom = 0;
  let left = range.getBoundingClientRect().left;
  while(current <= str.length) {
    range.setStart(node, current);
    if(current < str.length -1) {
      range.setEnd(node, current + 1);
    }
    const range_rect = range.getBoundingClientRect();
    bottom = range_rect.bottom;
    if(bottom > prevBottom) {
      lines.push({
        x: left - contLeft,
        y: prevBottom - contTop,
        text: str.substr(lastFound , current - lastFound)
      });
      prevBottom = bottom;
      lastFound = current;
      left = range_rect.left;
    }
    current++;
  }
  // push the last line
  lines.push({
    x: left - contLeft,
    y: bottom - contTop,
    text: str.substr(lastFound)
  });

  return lines;
}

function getRenderedTextLinesFromElement(elem) {
  elem.normalize();
  // first grab all TextNodes
  const nodes = [];
  const walker = document.createTreeWalker(
    elem, 
    NodeFilter.SHOW_TEXT
  );
  while(walker.nextNode()) {
    nodes.push(walker.currentNode);
  }
  // now get all their positions, with line breaks
  const elem_rect = elem.getBoundingClientRect();
  const top = elem_rect.top;
  const left = elem_rect.left;
  return nodes.reduce((lines, node) => 
    lines.concat(getLineBreaks(node, top, left)),
  []);
}

const ctx = canvas.getContext('2d');
ctx.textBaseline = 'bottom';
txt_area.oninput = e => {    
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0,0,canvas.width,canvas.height);
    
  const lines = getRenderedTextLinesFromElement(txt_area);
  // apply the div's style to our canvas
  const node_style = getComputedStyle(txt_area);
  const nodeFont = (prop) => node_style.getPropertyValue('font-' + prop);
  ctx.font = nodeFont('weight') + ' ' + nodeFont('size') + ' ' + nodeFont('family');
  ctx.textAlign = node_style.getPropertyValue('text-align');
  ctx.textBaseline = "bottom";
  // draw each line of text
  lines.forEach(({text, x, y}) => ctx.fillText(text, x, y));
};
txt_area.oninput();
#txt_area, canvas {
  width: 300px;
  height: 150px;
  resize: none;
  border: 1px solid;
  max-width: 300px;
  max-height: 150px;
  overflow: hidden;
}
canvas {
  border-color: green;
}
<div contenteditable id="txt_area">This is an example text
<br>that should get rendered as is in the nearby canvas
</div>
<canvas id="canvas"></canvas>

Dalam kasus Anda, Anda mungkin ingin membuat div ini disembunyikan, dan menghapusnya sesudahnya:

const text = "This is an example text with a few new lines\n" +
  "and some normal text-wrap.\n" +
  "\n" +
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n" +
  "\n" +
  "At tempor commodo ullamcorper a lacus.";
renderText(text);

function getLineBreaks(node, contTop = 0, contLeft = 0) {
  if(!node) return [];
  const range = document.createRange();
  const lines = [];
  range.setStart(node, 0);
  let prevBottom = range.getBoundingClientRect().bottom;
  let str = node.textContent;
  let current = 1;
  let lastFound = 0;
  let bottom = 0;
  let left = range.getBoundingClientRect().left;
  while(current <= str.length) {
    range.setStart(node, current);
    if(current < str.length -1) {
      range.setEnd(node, current + 1);
    }
    const range_rect = range.getBoundingClientRect();
    bottom = range_rect.bottom;
    if(bottom > prevBottom) {
      lines.push({
        x: left - contLeft,
        y: prevBottom - contTop,
        text: str.substr(lastFound , current - lastFound)
      });
      prevBottom = bottom;
      lastFound = current;
      left = range_rect.left;
    }
    current++;
  }
  // push the last line
  lines.push({
    x: left - contLeft,
    y: bottom - contTop,
    text: str.substr(lastFound)
  });

  return lines;
}

function getRenderedTextLinesFromElement(elem) {
  elem.normalize();
  // first grab all TextNodes
  const nodes = [];
  const walker = document.createTreeWalker(
    elem, 
    NodeFilter.SHOW_TEXT
  );
  while(walker.nextNode()) {
    nodes.push(walker.currentNode);
  }
  // now get all their positions, with line breaks
  const elem_rect = elem.getBoundingClientRect();
  const top = elem_rect.top;
  const left = elem_rect.left;
  return nodes.reduce((lines, node) => 
    lines.concat(getLineBreaks(node, top, left)),
  []);
}

function renderText(text) {
  // make the div we'll use to take the measures
  const elem = document.createElement('div');
  elem.classList.add('canvas-text-renderer');
  // if you wish to have new lines marked by \n in your input
  elem.innerHTML = text.replace(/\n/g,'<br>');
  document.body.append(elem);
  
  const ctx = canvas.getContext('2d');
  ctx.textBaseline = 'bottom';
  const lines = getRenderedTextLinesFromElement(elem);
  // apply the div's style to our canvas
  const node_style = getComputedStyle(elem);
  const nodeFont = (prop) => node_style.getPropertyValue('font-' + prop);
  ctx.font = nodeFont('weight') + ' ' + nodeFont('size') + ' ' + nodeFont('family');
  ctx.textAlign = node_style.getPropertyValue('text-align');
  ctx.textBaseline = "bottom";
  // draw each line of text
  lines.forEach(({text, x, y}) => ctx.fillText(text, x, y));

  // clean up
  elem.remove();
}
.canvas-text-renderer, canvas {
  width: 300px;
  height: 150px;
  resize: none;
  border: 1px solid;
  max-width: 300px;
  max-height: 150px;
  overflow: hidden;
}
canvas {
  border-color: green;
}
.canvas-text-renderer {
  position: absolute;
  z-index: -1;
  opacity: 0;
}
<canvas id="canvas"></canvas>
1
Kaiido 9 Agustus 2019, 03:06