2012-08-30 16 views
11

Digamos que tenemos un texto largo como Romeo & Juliet y queremos presentarlo en un eReader simple (sin animaciones, solo páginas y tamaño de letra personalizado). ¿Qué enfoques existen para conseguir esto?Divida el texto en páginas y presente por separado (HTML5)

Lo que he encontrado hasta el momento:

  • El uso de columnas CSS3 sería posible cargar todo el texto en la memoria peinado de una manera tal que una única columna toma el tamaño de una página entera . Hacer esto resultó ser extremadamente difícil de controlar y requiere que todo el texto se cargue en la memoria.
  • El uso de regiones css3 (no admitidas en ningún navegador importante) constituiría el mismo concepto básico que la solución anterior, con la gran diferencia de que no sería tan difícil de controlar (ya que cada 'columna' es un elemento autónomo)
  • Dibujar el texto en un lienzo le permitirá saber exactamente dónde termina el texto y, por lo tanto, dibujar la siguiente página en función de eso. Una de las ventajas es que solo necesita cargar todo el texto hasta la página actual (aún está mal, pero es mejor). La desventaja es que no se puede interactuar con el texto (como seleccionar el texto).
  • Coloque cada palabra dentro de un elemento y déle a cada elemento un ID único (o guarde una referencia lógica en javascript), luego use document.elementFromPoint para encontrar el elemento (palabra) que es el último en la página y muestre la siguiente página en adelante de esa palabra A pesar de ser el único que me parece realmente realista, la sobrecarga generada por esto tiene que ser inmensa.

Sin embargo, ninguno de ellos parece ser aceptable (primero no dio suficiente control para hacerlo funcionar, segundo no es compatible, tercero es difícil y sin texto y cuarto da una ridícula sobrecarga) , por lo tanto, cualquier buen enfoque que no haya pensado aún, o formas de resolver una o más desventajas de los métodos mencionados (sí, soy consciente de que esta es una pregunta bastante abierta, pero cuanto más abierta sea, mayores serán las posibilidades de produciendo respuestas relevantes)?

+0

¿Estás intentando paginar el texto, pero en el navegador? ¿O estamos hablando de un e-reader dedicado aquí? – Eric

+0

Vamos a mantenerlo en el texto paginado en el navegador (aunque técnicamente estoy trabajando en un proyecto de teléfono). –

+0

Sobre la recompensa: ya no está trabajando en este proyecto, pero tal vez ya es posible, así que me sentí como si comenzara una recompensa. –

Respuesta

5

Ver my answer a Wrap text every 2500 characters in a for pagination using PHP or javascript.Terminé con http://jsfiddle.net/Eric/WTPzn/show

Citando el post original lo mejor que pueda: (blockquotes y bloques de código no jugar bien juntos)

acaba de establecer su HTML a:

<div id="target">...</div>` 

Añadir un poco de CSS para páginas:

#target {white-space: pre-wrap; /* respect line breaks */} 
    .individualPage {border: 1px solid black;padding: 5px;} 

Y a continuación, utilizar el siguiente código:

var contentBox = $('#target'); 
    //get the text as an array of word-like things 
    var words = contentBox.text().split(' '); 

    function paginate() { 
     //create a div to build the pages in 
     var newPage = $('<div class="individualPage" />'); 
     contentBox.empty().append(newPage); 

     //start off with no page text 
     var pageText = null; 
     for(var i = 0; i < words.length; i++) { 
      //add the next word to the pageText 
      var betterPageText = pageText ? pageText + ' ' + words[i] 
              : words[i]; 
      newPage.text(betterPageText); 

      //Check if the page is too long 
      if(newPage.height() > $(window).height()) { 
       //revert the text 
       newPage.text(pageText); 

       //and insert a copy of the page at the start of the document 
       newPage.clone().insertBefore(newPage); 

       //start a new page 
       pageText = null; 
      } else { 
       //this longer text still fits 
       pageText = betterPageText;    
      } 
     }  
    } 

    $(window).resize(paginate).resize(); 
+0

Malo, en el día que intenté ejecutar su implementación en el texto completo de Romeo & Juliet y se bloqueó mi navegador constantemente. Lo he vuelto a intentar ahora y tengo que decir que al menos llega allí, aunque con bastante tiempo de carga. –

+0

Ok, esto es incómodo, estaba planeando al final otorgar la recompensa a esta respuesta, pero estuvo ausente durante 2 días y se perdió el período de 24 horas. –

-4

Eso es simple, y no es necesario javascript. El paged media type es compatible desde CSS2. Consulte http://www.w3.org/TR/CSS21/page.html (o current CSS3 module) para ver las propiedades compatibles.

+1

Señor, eso solo especifica la forma en que los medios paginados deben ser diseñados y manejados por un agente de usuario. Esto no ayuda de ninguna manera a mostrar los medios de búsqueda en el navegador hasta donde puedo ver. –

+0

Un e-reader que usa navegación paginada usará este CSS. Incluso puede usar medios paginados en el navegador, p. para compilar [presentaciones de diapositivas a pantalla completa] (http://dev.opera.com/articles/view/html-css-slideshows/) – Bergi

+0

Opera decidió implementar la especificación de proyección de tal manera que se aplicará al diseño de un página web cuando está en modo de pantalla completa. Esto no es lo mismo que una solución técnica para crear presentaciones de pantalla completa, ya que otros navegadores no actúan como un agente de usuario de proyección (por ejemplo, en Firefox se puede instalar una extensión y en otros es completamente imposible). –

5

SVG podría ser una buena opción para su paginación texto

  • texto SVG es en realidad el texto - a diferencia de la lona la cual muestra solo una imagen de texto.

  • El texto SVG es legible, seleccionable, buscable.

  • El texto SVG no se envuelve automáticamente de forma nativa, pero esto se remedia fácilmente mediante javascript.

  • Los tamaños de página flexibles son posibles porque el formateo de la página se realiza en javascript.

  • La paginación no depende del formateo dependiente del navegador.

  • Las descargas de texto son pequeñas y eficientes. Solo se debe descargar el texto de la página actual.

Aquí están los detalles de cómo SVG paginación se puede hacer y una demostración:

http://jsfiddle.net/m1erickson/Lf4Vt/

enter image description here

Parte 1: eficientemente traiga sobre una página valor de las palabras desde una base de datos en el servidor

Almacena el texto completo en una base de datos con 1 palabra por fila.

Cada fila (palabra) se indexa secuencialmente por el orden de la palabra (palabra # 1 tiene índice == 1, palabra # 2 tiene índice == 2, etc.).

Por ejemplo, esto se vendería todo el texto en el buen orden de las palabras:

// select the entire text of Romeo and Juliet 
// “order by wordIndex” causes the words to be in proper order 

Select word from RomeoAndJuliet order by wordIndex 

Si asume ninguna página tiene contiene alrededor de 250 palabras al formato, a continuación, esta consulta la base de datos va a buscar a los primeros 250 palabras de texto para página n. ° 1

// select the first 250 words for page#1 

Select top 250 word from RomeoAndJuliet order by wordIndex 

¡Ahora la parte buena!

Digamos que la página n. ° 1 usó 212 palabras después del formateo. Luego, cuando esté listo para procesar la página n. ° 2, podrá buscar otras 250 palabras a partir de la palabra n. ° 213. Esto da como resultado búsquedas de datos rápidas y eficientes.

// select 250 more words for page#2 
// “where wordIndex>212” causes the fetched words 
// to begin with the 213th word in the text 

Select top 250 word from RomeoAndJuliet order by wordIndex where wordIndex>212 

Parte 2: Formato de las palabras captan en líneas de texto que se ajusten a la página especificada ancho

Cada línea de texto debe contener suficientes palabras para llenar la página especificada con, pero no más.

Comience la línea n. ° 1 con una sola palabra y luego agregue las palabras 1 a la vez hasta que el texto encaje en el ancho de página especificado.

Después de que se instala la primera línea, nos movemos hacia abajo por una línea de altura y comenzamos la línea n. ° 2.

Ajustar las palabras en la línea requiere medir cada palabra adicional agregada en una línea. Cuando la siguiente palabra excedería el ancho de línea, esa palabra adicional se moverá a la siguiente línea.

Una palabra puede medirse utilizando el método Html Canvases context.measureText.

Este código tomará un conjunto de palabras (como las 250 palabras extraídas de la base de datos) y formateará la mayor cantidad de palabras posible para llenar el tamaño de la página.

maxWidth es el ancho máximo de píxeles de una línea de texto.

maxLines es la cantidad máxima de líneas que cabrán en una página.

function textToLines(words,maxWidth,maxLines,x,y){ 

    var lines=[]; 

    while(words.length>0 && lines.length<=maxLines){ 
     var line=getOneLineOfText(words,maxWidth); 
     words=words.splice(line.index+1); 
     lines.push(line); 
     wordCount+=line.index+1; 
    } 

    return(lines); 
} 

function getOneLineOfText(words,maxWidth){ 
    var line=""; 
    var space=""; 
    for(var i=0;i<words.length;i++){ 
     var testWidth=ctx.measureText(line+" "+words[i]).width; 
     if(testWidth>maxWidth){return({index:i-1,text:line});} 
     line+=space+words[i]; 
     space=" "; 
    } 
    return({index:words.length-1,text:line}); 
} 

Parte 3: Muestra las líneas de texto usando SVG

el elemento de texto SVG es un verdadero elemento html que puede ser leído, y buscó seleccionado.

Cada línea de texto individual en el elemento de texto SVG se visualiza con un elemento SVG Tspan.

Este código toma las líneas de texto formateadas en la Parte # 2 y muestra las líneas como una página de texto usando SVG.

function drawSvg(lines,x){ 
    var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 
    var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 
    sText.setAttributeNS(null, 'font-family', 'verdana'); 
    sText.setAttributeNS(null, 'font-size', "14px"); 
    sText.setAttributeNS(null, 'fill', '#000000'); 
    for(var i=0;i<lines.length;i++){ 
     var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); 
     sTSpan.setAttributeNS(null, 'x', x); 
     sTSpan.setAttributeNS(null, 'dy', lineHeight+"px"); 
     sTSpan.appendChild(document.createTextNode(lines[i].text)); 
     sText.appendChild(sTSpan); 
    } 
    svg.appendChild(sText); 
    $page.append(svg); 
} 

Aquí es código completo en caso de que la demo de enlace se rompe:

<!doctype html> 
<html> 
<head> 
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css --> 
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script> 
<style> 
    body{ background-color: ivory; } 
    .page{border:1px solid red;} 
</style> 
<script> 
$(function(){ 

    var canvas=document.createElement("canvas"); 
    var ctx=canvas.getContext("2d"); 
    ctx.font="14px verdana"; 

    var pageWidth=250; 
    var pageHeight=150; 
    var pagePaddingLeft=10; 
    var pagePaddingRight=10; 
    var approxWordsPerPage=500;   
    var lineHeight=18; 
    var maxLinesPerPage=parseInt(pageHeight/lineHeight)-1; 
    var x=pagePaddingLeft; 
    var y=lineHeight; 
    var maxWidth=pageWidth-pagePaddingLeft-pagePaddingRight; 
    var text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; 

    // # words that have been displayed 
    //(used when ordering a new page of words) 
    var wordCount=0; 

    // size the div to the desired page size 
    $pages=$(".page"); 
    $pages.width(pageWidth) 
    $pages.height(pageHeight); 


    // Test: Page#1 

    // get a reference to the page div 
    var $page=$("#page"); 
    // use html canvas to word-wrap this page 
    var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y); 
    // create svg elements for each line of text on the page 
    drawSvg(lines,x); 

    // Test: Page#2 (just testing...normally there's only 1 full-screen page) 
    var $page=$("#page2"); 
    var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y); 
    drawSvg(lines,x); 

    // Test: Page#3 (just testing...normally there's only 1 full-screen page) 
    var $page=$("#page3"); 
    var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y); 
    drawSvg(lines,x); 


    // fetch the next page of words from the server database 
    // (since we've specified the starting point in the entire text 
    // we only have to download 1 page of text as needed 
    function getNextWords(nextWordIndex){ 
     // Eg: select top 500 word from romeoAndJuliet 
     //  where wordIndex>=nextwordIndex 
     //  order by wordIndex 
     // 
     // But here for testing, we just hardcode the entire text 
     var testingText="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; 
     var testingWords=testingText.split(" "); 
     var words=testingWords.splice(nextWordIndex,approxWordsPerPage); 

     // 
     return(words);  
    } 


    function textToLines(words,maxWidth,maxLines,x,y){ 

     var lines=[]; 

     while(words.length>0 && lines.length<=maxLines){ 
      var line=getLineOfText(words,maxWidth); 
      words=words.splice(line.index+1); 
      lines.push(line); 
      wordCount+=line.index+1; 
     } 

     return(lines); 
    } 

    function getLineOfText(words,maxWidth){ 
     var line=""; 
     var space=""; 
     for(var i=0;i<words.length;i++){ 
      var testWidth=ctx.measureText(line+" "+words[i]).width; 
      if(testWidth>maxWidth){return({index:i-1,text:line});} 
      line+=space+words[i]; 
      space=" "; 
     } 
     return({index:words.length-1,text:line}); 
    } 

    function drawSvg(lines,x){ 
     var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 
     var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 
     sText.setAttributeNS(null, 'font-family', 'verdana'); 
     sText.setAttributeNS(null, 'font-size', "14px"); 
     sText.setAttributeNS(null, 'fill', '#000000'); 
     for(var i=0;i<lines.length;i++){ 
      var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); 
      sTSpan.setAttributeNS(null, 'x', x); 
      sTSpan.setAttributeNS(null, 'dy', lineHeight+"px"); 
      sTSpan.appendChild(document.createTextNode(lines[i].text)); 
      sText.appendChild(sTSpan); 
     } 
     svg.appendChild(sText); 
     $page.append(svg); 
    } 

}); // end $(function(){}); 
</script> 
</head> 
<body> 
    <h4>Text split into "pages"<br>(Selectable & Searchable)</h4> 
    <div id="page" class="page"></div> 
    <h4>Page 2</h4> 
    <div id="page2" class="page"></div> 
    <h4>Page 3</h4> 
    <div id="page3" class="page"></div> 
</body> 
</html> 
+0

Simplemente ejecuté este código (después de los cambios para generar las páginas dinámicamente) contra el texto completo de Romeo & Juliet y se bloqueó mi navegador. –

2

Tengo una solución con bastante simple, marcado css cambiable y 3 funciones js bastante cortos.

Primero he creado dos elementos div, de los cuales uno está oculto pero contiene el texto completo y el otro se muestra pero está vacío todavía. El HTML se vería así:

<div id="originalText"> 
some text here 
</div> 
<div id="paginatedText"></div> 

la CSS de estos dos es:

#originalText{ 
    display: none; // hides the container 
} 

#paginatedText{ 
    width: 300px; 
    height: 400px; 
    background: #aaa; 
} 

También hice el css listo para una página de nombres de clase que se parece a esto:

.page{ 
    padding: 0; 
    width: 298; 
    height: 398px; // important to define this one 
    border: 1px solid #888; 
} 

la parte realmente importante es definir la altura porque de lo contrario las páginas se alargarán cuando completemos las palabras más adelante.


Ahora viene la parte importante. Las funciones JavaScript. Los comentarios deberían hablar por sí mismos.

function paginateText() { 
    var text = document.getElementById("originalText").innerHTML; // gets the text, which should be displayed later on 
    var textArray = text.split(" "); // makes the text to an array of words 
    createPage(); // creates the first page 
    for (var i = 0; i < textArray.length; i++) { // loops through all the words 
     var success = appendToLastPage(textArray[i]); // tries to fill the word in the last page 
     if (!success) { // checks if word could not be filled in last page 
      createPage(); // create new empty page 
      appendToLastPage(textArray[i]); // fill the word in the new last element 
     } 
    } 
} 

function createPage() { 
    var page = document.createElement("div"); // creates new html element 
    page.setAttribute("class", "page"); // appends the class "page" to the element 
    document.getElementById("paginatedText").appendChild(page); // appends the element to the container for all the pages 
} 

function appendToLastPage(word) { 
    var page = document.getElementsByClassName("page")[document.getElementsByClassName("page").length - 1]; // gets the last page 
    var pageText = page.innerHTML; // gets the text from the last page 
    page.innerHTML += word + " "; // saves the text of the last page 
    if (page.offsetHeight < page.scrollHeight) { // checks if the page overflows (more words than space) 
     page.innerHTML = pageText; //resets the page-text 
     return false; // returns false because page is full 
    } else { 
     return true; // returns true because word was successfully filled in the page 
    } 
} 

Al final acabo llama la función paginateText con

paginateText(); 

Todo este skript trabaja para cada texto y para cada estilo de las páginas.

Para que pueda cambiar la fuente y el tamaño de la fuente e incluso el tamaño de las páginas.

También tengo un jsfiddle con todo allí.

Si he olvidado algo o si tiene alguna pregunta, no dude en comentar y hacer sugerencias o hacer preguntas.

2

No tengo suficientes representantes para hacer un comentario, pero solo quería decir que la respuesta de Eric funciona muy bien. Estoy creando un eReader, excepto que lee archivos HTML, y puede usarlo para texto no listo para publicación. Hay dos páginas que se pueden ver y cambian de tamaño solo cuando presiona un botón.

Hice muchas modificaciones. Sin embargo, solo había un pequeño error que encontré. Cuando comprueba si la última palabra cae fuera del borde de la página, y lo hace, debe agregar esa palabra nuevamente a la lista. En pocas palabras, en el primer caso de la declaración if, ponga en la línea i--; para regresar y poner esa palabra en la página siguiente.

Aquí está mi modificaciones:

  1. hizo todo en una función, con los argumentos (contenido, objetivo).
  2. agregué un BackUpContent variable, para reutilizarlo cuando cambio el tamaño de las páginas.
  3. cambió la nueva página a una página de prueba invisible y agregó una página de matriz [i], que contiene el contenido de cada página, para ir y volver fácilmente después de ordenar las páginas.
  4. agregó la línea "pC++;", un contador de páginas, a la primera parte de la declaración else.
  5. cambiado.texto a .html, para que no cuente las etiquetas como sus equivalentes de texto.
  6. Lo diseñé alrededor de 1 o 2 div con contenido cambiante, en lugar de muchos, muchos divs que se ocultan y muestran.
  7. Hay más insertos que aún no he tenido.

Si quieres mantener algo así como los párrafos enteros en la misma página, cambie la línea

pageText + ' ' + words[i] 

a

pageText + '</p><p>' + words[i] 

y la línea

words = content.split(' '); 

a

words = content.split('</p><p>'); 

Pero solo debe usar eso si está seguro de que cada uno de los elementos es lo suficientemente pequeño como para ir en una página.

La solución de Eric es exactamente la pieza que me faltaba. Iba a hacer mi propia pregunta, pero finalmente encontré esta página en las sugerencias después de escribir casi toda mi pregunta. La redacción de la pregunta es un poco confusa, sin embargo.

Gracias Eric!

+0

Es bueno saber que te ayudó mucho: D En cuanto a la corrección de errores que mencionas, puedes editarla directamente en la publicación de Eric.Será revisado por pares, así que asegúrese de dejar en claro en la descripción lo que está haciendo, pero debe ser aceptado de manera razonablemente rápida. –

Cuestiones relacionadas