¿Por qué Text.splitText () afecta el layout?

Digamos que tenemos un párrafo en nuestra página, con un solo bloque de text.

<p>laborum beatae est nihil, non hic ab, deserunt repellat quas. Est molestiae ipsum minus nesciunt tempore voluptate laboriosam</p> 

DOM-wise, la estructura es:

 HTMLParagraphElement Text [laborum beatae est nihil...] 

Ahora lo dividimos (con Text.splitText() ) dos veces, para separar el fragment "deserunt repellat quas. Est" . La estructura se convierte en:

 HTMLParagraphElement Text [laborum beatae est nihil...] Text [deserunt repellat quas. Est] Text [ molestiae ipsum minus nesciunt...] 

Si bien esta operación afecta a DOM, nunca la cambia en el nivel Elemento (Text! == Element), por lo que no esperaba cambios visuales.

Sin embargo, splitText() afecta al layout, lo que activa tanto el relayout como el repintado en todos los browseres probados (Chrome 60, Firefox 55, Edge 14, todo en el sistema operativo Windows 10). Lo mismo ocurre cuando llamamos a ParagraphElement.normalize() , networkinguciendo la cantidad de nodos de text a 1; nuevamente se desencadenan tanto retransmisión como repintado.

Hay un desagradable efecto secundario de esto, que se puede ver en esta demostración . Si revisa las palabras cerca de 'quas. Est, ¡ves que realmente cambian de position!

Es claramente visible en Firefox, y es mucho más sutil (aunque también distinguible) en Chrome. Para mi diversión, no se produjo ese "baile de palabras" en Edge.

La razón por la que esto es importante se muestra en esta demostración de (tipo de) motor de selección ajustado. Esta versión particular no funcionará en Firefox (aún no es compatible con caretRangeFromPoint – argh!), Pero incluso con "point2dom" reconectado en caretPositionFromPoint el text resaltado se reposiciona allí, tanto en Chrome como, lo que es peor. De nuevo, parece funcionar bien en Edge.

Entonces, de hecho, estoy más que interesado en entender las razones y encontrar las soluciones.

Aquí está el gif animado que muestra cómo se reproduce la primera demo en Chrome (solo disparo un clic en el intervalo)

cuando las palabras marchan en ...

El temblor ES sutil aquí, pero aún se puede observar en todas las palabras. Estoy especialmente desconcertado por el motivo por el cual i molestiae batidos, ya que las letras que las rodean parecen permanecer donde están.

Y empeora (mucho peor) con fonts less comunes y más text, como en la demostración de selección.

Cambiando a font-family:monospace no resolvió esto, pero lo hizo aparentemente peor:

enter image description here

Cambiar font-kerning a none tampoco ayudó.

ACTUALIZACIÓN: el problema está registrado en el rastreador Blink.

Acerca de la retransmisión / repintado es de esperar, ya que los nodos de text también son nodos DOM … No son elementos DOM, pero los browseres tienen que reconsiderar el layout, incluso si es de esperar que permanezca igual, pueden tener que movimiento. Tal vez debido a kerning.

Ahora, ¿por qué dividir text causa algo de movimiento? Lo que espero es que los browseres dibujen las partes de text por separado. Dos letras vecinas generalmente tienen un espacio que puede ser networkingucido por la fuente dependiendo de las letras, tome "WA" por ejemplo, el final de la W está por encima del inicio de la A, que se llama kerning (Thx Ismael Miguel). Cuando los nodos de text se dibujan por separado, cada uno debe terminar antes de los siguientes comienzos, por lo que puede crear un espacio más grande entre esas letras, ya que previene el interletraje.

Lo siento, el espacio entre letras tiene un nombre, pero lo olvidé …

 .one { background-color: #FF9999; } .two { background-color: #99FF99; } body { font-size: 40px; } div>span { border: 1px solid black; } 
 <div><span>AW</span> - in the same DOM node.</div> <div><span><span>A</span><span>W</span></span> - in two different nodes</div> <div><span><span class="one">A</span><span class="two">W</span></span> - in two different nodes, colonetworking</div> 

tl: versión dr

splitText() puede ser la acción que está desencadenando el cambio, pero el cambio está siendo causado por la dom actualizada que se ejecuta a través del motor de justificación de text. Cambiar de text-align: justify; para text-align: left; para ver lo que quiero decir

Apéndice

Eso maneja las palabras cambiando. En cuanto a las letras que cambian de lugar cuando justify está desactivado, es un poco más difícil de cuantificar, pero parece ser un umbral de networkingondeo que se está cruzando.

Respuesta completa

Consideraciones en la justificación del text

La justificación del text es complicada de implementar por decir lo less. Para simplificar, hay tres consideraciones básicas:

  1. Exactitud
  2. Estética
  3. Velocidad

Una ganancia en cualquiera, requiere una pérdida en uno o ambos de los otros. Por ejemplo, InDesign, que favorece la estética, usa espaciado de palabras (positivo y negativo), espaciado entre letras (positivo y negativo) y considera todas las líneas de un párrafo para encontrar el layout más agradable a un costo de velocidad, y permite alignment de los márgenes a costa de la precisión. Pero debido a que solo atraviesa todo esto una vez y guarda los resultados en el file, puede salirse con la suya siendo (relativamente) muy lento.

Los browseres tienden a preocuparse mucho más por la velocidad, en parte porque necesitan poder justificar el text rápidamente en hardware desactualizado, pero también porque, gracias a la interactividad que ahora disfrutamos, a veces necesita volver a justificar el mismo bloque de text. veces durante una session.

Varianza en la implementación del browser

La especificación es algo vaga sobre el tema de la justificación, diciendo cosas como:

Al justificar text, el agente de usuario toma el espacio restante entre los extremos del contenido de una línea y los bordes de su cuadro de línea, y distribuye ese espacio a lo largo de su contenido para que el contenido llene exactamente el cuadro de línea. El agente de usuario puede distribuir alternativamente espacio negativo, colocando más contenido en la línea de lo que de otra forma encajaría en condiciones de espaciamiento normales.

Y

Para la justificación automática, esta especificación no define cuáles son todas las oportunidades de justificación, cómo se priorizan o cuándo y cómo interactúan múltiples niveles de oportunidades de justificación.

Como resultado, todos los browseres pueden optimizar esta funcionalidad como mejor les parezca.

Un ejemplo

Un motor de justificación de text moderno es más complicado de lo que se puede explicar razonablemente en el espacio que tenemos aquí. Agregue que están continuamente ajustados para encontrar un mejor equilibrio entre las consideraciones principales y todo lo que escribo aquí estará desactualizado en unos pocos nanosegundos de todos modos, voy a utilizar un algorithm de justificación de text muy antiguo (y mucho más simple) para demostrar cómo un motor puede tener dificultades para renderizarse consistentemente en esta situación.

Supongamos que tenemos la cadena 'Lorem ipsum dolor sit amet, consectetur.' y podemos caber 35 caracteres en una línea. Entonces usemos este algorithm de justificación:

  1. Crear una matriz
  2. Trabajando a través de la cadena hasta que encuentre un final de palabra o signo de puntuación seguido de un espacio o un final de cadena
  3. Cuando encuentre un final de palabra, verifique si la longitud de la palabra + la longitud de todas las palabras en la matriz más el número de oportunidades de justificación. Si es así, recorte el hilo y colóquelo en el set.
  4. Cuando no se puedan agregar más palabras a la matriz, tome la diferencia entre el espacio necesario y el espacio disponible, divida por el número de oportunidades de justificación y dibuje la matriz, colocando esa cantidad de espacio entre cada palabra.

Usando este algorithm:

  1. Dividir la cadena

     'Lorem ipsum dolor sit amet, consectetur.' => ['Lorem','ipsum','dolor','sit','amet,'] & 'consectetur.' 
  2. Con 23 caracteres de ancho de text y 35 caracteres de espacio disponible, agregamos 3 espacios entre cada palabra (estoy cuadruplicando el espacio para énfasis, que será importante más adelante)

     ------------------------------------------------------------------------- |Lorem ipsum dolor sit amet,| |consectetur. | ------------------------------------------------------------------------- 

Esto es rápido porque podemos dibujar todo de izquierda a derecha sin necesidad de retroceder, y no necesitamos mirar hacia adelante.

Si ejecutamos textSplit en esto y efectivamente lo convertimos en una matriz:
['Lorem ipsum dolor ','sit',' amet, consectetur.']
Entonces, necesitaríamos modificar nuestras reglas, vamos a cambiar la regla 2 para que funcione a través de cada cadena en la matriz, siguiendo las mismas reglas que antes.

  1. Divida las strings, tenga en count que hay un espacio antes de amet, por lo que el límite de palabras no lo atrapará

     `['Lorem ipsum dolor ','sit',' amet, consectetur.']` => ['Lorem','ipsum','dolor','sit',' amet,'] & 'consectetur.' 
  2. Con 24 caracteres de ancho de text y 35 caracteres de espacio disponible, agregamos 2,75 espacios entre cada palabra (una vez más cuadruplicando el espacio). El espacio extra en la string amet también se dibuja.

     ------------------------------------------------------------------------- |Lorem ipsum dolor sit amet,| |consectetur. | ------------------------------------------------------------------------- 

Si miramos que las dos líneas laterales están del lado, podemos ver la diferencia.

  ------------------------------------------------------------------------- a |Lorem ipsum dolor sit amet,| b |Lorem ipsum dolor sit amet,| ------------------------------------------------------------------------- 

Nuevamente, estos son exagerados, un cuarto de espacio en la vida real esto solo sería un píxel o dos.

Nuestro set de reglas es muy simple, por lo que podríamos resolver este problema muy fácilmente.

Considere cuán complicada sería esa debugging cuando tenga un motor de justificación que tenga que soportar:

  1. Otras properties CSS (ajustables) para justificación
  2. Múltiples idiomas y todas sus reglas
  3. Fuentes que tienen:
    • Anchos de caracteres variables
    • Métricas de Kerning
    • vectores que no necesariamente se alinean con un píxel
  4. Elementos en línea (y en línea) que tienen su propio estilo
  5. …incesantemente

Sin mencionar, la mayor parte de esto se transfiere a la GPU para dibujar.

De todos modos todo esto para decir

Tenga en count que, de hecho, está alterando el dom y obligando a todo el bloque a volver a renderizar como resultado. Dada la cantidad de factores involucrados, es muy difícil esperar que dos estructuras dom diferentes siempre den exactamente lo mismo.

Apéndice

En cuanto a las letras que parecen cambiar de vez en cuando un poco, la mayoría de lo que he dicho sobre la complejidad de cómo se manejan estas cosas se sigue aplicando a lo siguiente.

De nuevo, en un esfuerzo por mejorar la velocidad, los numbers a menudo se networkingondean antes de pasarlos a la GPU para su renderizado.

Proporcionando un ejemplo simplificado, una centésima de un pixel no hace mucha diferencia, sería imperceptible para el ojo humano y, por lo tanto, es un desperdicio de poder de procesamiento. Entonces decides networkingondear al pixel más cercano.

Digamos que la secuencia de dibujar personajes es:

  1. Tome el comienzo del contenedor
  2. Agregue el desplazamiento
  3. Dibuja el personaje en esta location, networkingondeado al píxel más cercano
  4. Actualice el desplazamiento agregando el ancho no networkingondeado del carácter.

Personajes con anchos

 10.2 10.3 10.4 10.2 10.6 11 8.9 9.9 7.6 9.2 9.8 10.4 10.4 11.1 10.5 10.5 

Comenzando en el punto 0 dibujará en:

 0 10 21 31 41 52 63 72 82 89 98 108 119 129 140 151 

Pero, ¿y si llegas al final de un nodo? Bueno, eso es fácil, solo comienza el nuevo nodo en la siguiente position de dibujo y continúa?

Personajes con anchos

 10.2 10.3 10.4 10.2 10.6 11 8.9|9.9 7.6 9.2 9.8 10.4 10.4 11.1 10.5 10.5 

Comenzando en el punto 0 dibujará en:

 0 10 21 31 41 52 63 72 82 89 99 108 119 129 140 151 

Aunque los numbers subyacentes son diferentes, la location del renderizado sigue siendo la misma para cada location excepto el 11º carácter debido al networkingondeo.

Puede que no sea necesariamente la position de inicio, una vez más, aquí hay una gran cantidad de complejidad. Los síntomas apuntan a un umbral de networkingondeo de algún tipo. Como dije antes, cuando se renderizan dos treees dom diferentes, se deben esperar diferencias.