CAD-in-a browser, HTML5 Toile et limitations de performances du Javascript / développement de navigateur

Je crée une application de CAO qui fonctionne dans un navigateur.

  • CAD représente Computer Aided Design .
  • Illustrator, CorelDraw, AutoCAD etc sont quelques exemples d'applications de CAO.

Il est basé sur Paper.js , une bibliothèque de toile très propre qui vous permet de manipuler des vecteurs.

Paper.js est plus que génial. Il existe une pléthore de fonctionnalités à utiliser, les créateurs sont vraiment utiles pour répondre aux questions / la correction des bogues et c'est généralement une bibliothèque très facile à utiliser et professionnelle. Les nouvelles fonctionnalités sont également disponibles à un bon rythme et il est facile de mettre à jour vers de nouvelles versions de la bibliothèque.

Le problème principal que j'ai en ce moment consiste à redessiner les performances du cycle.

L'algorithme de redessiner est «stupide» (en termes de hacks intelligents pour améliorer les performances) et donc inefficace et lent


Le problème principal est que les coordonnées de rendu dépendent d'un cycle de redimensionnement progressivement plus lent.

Au fur et à mesure que les points d'attraction s'accumulent, chaque cycle de redessinage devient plus lent et plus lent.

L'algorithme de redessiner est aussi simple que possible:

  • Nettoyer la zone
  • Prenez tous les points sauvegardés (scène actuelle)
  • Redessinez tous les points enregistrés.

La question

Y a-t-il des exemples en classe d'optimisations de rendu dans de tels cas – en supposant que je souhaiterais éviter de mettre en œuvre un algorithme de rectangles sales (dessiner uniquement les zones qui ont changé).

EG: j'ai pensé à traquer les chemins qui ne sont pas directement sélectionnés et travaillés, enregistrer leurs données de chemin dans LocalStorage et dès que l'utilisateur clique sur eux pour les manipuler, pour réimporter leurs données de chemin d'accès. Cela réduirait au minimum l'utilisation de la mémoire sur des éléments non traités.

Rasteriser, sauvegarder et reimporter est également un goulot d'étranglement. Donc, cette solution est moins optimale.

Edit: J'ai expérimenté avec la rasterisation manuelle sur le site qui fonctionne très bien, j'ai posté une réponse ci-dessous.

J'aimerais avoir plus d'idées comme celle décrite ci-dessus.

Outil utile

Ma branche de papier.js pourrait vous aider, mais ce n'est peut-être pas le meilleur pour vous.

Il vous permet d'empêcher paper.js de redessiner tout les cadres (utilisez paper.view.persistence = 1; ).

De cette façon, vous avez un meilleur contrôle sur ce qu'il faut effacer et devrait être redessiné: par exemple, lorsque vous déplacez une forme, vous pouvez effacer la zone où elle se trouvait (en utilisant DrawRect Canvas native) et la mettre à jour une fois qu'elle a été déplacée (utilisez le path.needsUpdate(); ).

Inconvénient

Les problèmes surviennent lorsque les formes se croisent. Si vous souhaitez modifier une forme qui croise une autre, vous devrez mettre à jour les deux. Même chose si la deuxième forme croise un troisième, et ainsi de suite.

Donc, vous avez besoin d'une fonction récursive, pas difficile à coder, mais il peut être coûteux s'il existe de nombreuses formes complexes qui se croisent, et donc vous ne pourriez pas avoir de performances dans ce cas.

(Mise à jour) Mise en cache bitmap

Comme l'a suggéré Nicholas Kyriakides dans la réponse suivante , la mise en cache Bitmap est une très bonne solution.

Une toile par forme

Une alternative serait de dessiner chaque forme sur une toile distincte (fonctionnant en couches). De cette façon, vous pouvez librement effacer et redessiner chaque forme de manière indépendante. Vous pouvez détacher l'événement onFrame des vues qui ne changent pas (tout le canevas sauf celui sur lequel l'utilisateur fonctionne). Cela devrait être plus facile, mais cela entraîne d'autres petits problèmes tels que le partage des mêmes paramètres de vue de projet (dans le cas du zoom), et il pourrait être coûteux avec de nombreuses formes (ce qui signifie beaucoup de canevas).

Toile statique et dynamique

Une (probablement) meilleure approche serait d'avoir seulement deux toiles, une pour les formes statiques, et une pour la forme active. Le canevas de formes statiques contiendrait toutes les formes (s'attendre à ce que l'on édite) et serait redessiné juste lorsque l'utilisateur démarre et interrompt l'édition de la forme active. Lorsque l'utilisateur commence à éditer une forme, il sera transféré de la toile statique à la dynamique, et l'autre façon lorsque l'utilisateur s'arrête.

Cela peut être fait avec la rasterisation dans un processus / technique similaire à la mise en cache bitmap .

Le problème avec les conceptions du nombre de nœuds élevés est que le rendu entraîne un gémissement du moteur de rendu. Le navigateur doit traverser ses nœuds et ouvrir ces pixels sur le canevas.

Voici donc une bonne solution:


1. Rendez un bitmap mais gardez la forme originale ci-dessous, cachée

La solution consiste à remplacer les vecteurs par des images, en les rasterisant – uniquement lors du rendu, tout en conservant la forme originale sous sa copie d'image, dans un état caché uniquement lorsqu'il est inactif ( actuellement pas manipulé ).

En cliquant sur les images – nous les supprimes et allumons la visibilité de la forme originale. De cette façon, les formes inactives sont rendues alors que les images et les formes actives sont libérées de leur représentation bitmap et agissent comme des vecteurs, libres d'être manipulés. Lorsqu'elles ne sont pas actives, elles se sentent là, invinibles, avec leur copie Raster sur elles.

Cela permet au moteur de conserver la représentation vectorielle des formes, mais évite de les rendre en tant que vecteurs – au lieu de cela, des images semblables à celles-ci sont superposées.

Les 1000 commandes de chemin sont essentiellement remplacées par une seule image – mais uniquement lors du rendu – le chemin d'origine existe réellement comme un objet dans le Graphique de scène ou quel que soit le type de DOM que vous utilisez

2. Rasteriser en groupes

L'astuce consiste à effectuer la rasterisation en groupes – les formes du groupe 10-15 ensemble et à les rasteriser en une seule image. Cela réduit le nombre de trames faible. En cliquant sur une image – nous pouvons libérer tout le groupe ou simplement l'élément sur lequel on a cliqué.

3. Joignez les gestionnaires de clics sur le groupe pour rétablir la copie vectorielle lors de la réactivation

Lors du rasterisation d'un groupe, nous pouvons simplement attacher un gestionnaire de click sur celui-ci, alors lorsque vous cliquez, nous basculons bitmap avec vecteur. Les images ne se comportent pas comme des vecteurs lorsqu'ils sont testés – les images sont des squares par nature et ne peuvent pas être testées de manière asymétrique. Alors qu'un vecteur considère que ses arêtes sont sur ses limites de chemin d'accès – une image considère que ses limites sont la boîte de délimitation complète. La solution consiste à faire un clic sur l'image pour réussir à tester le point de clics avec le chemin vecteur sous l'image – si elle renvoie true puis effectue une version.

Les autres sont des optimisations et rendent l'ensemble intelligent. Actuellement, je permet à mes utilisateurs d'appuyer sur un bouton et d'effectuer cette optimisation sur toutes les formes du canevas. Peut-être que cela se fait de manière plus automatique / intuitive.

Quoi qu'il en soit, voici le code pour cela, mais cela ne s'applique que dans Paper.js. L'ensemble du concept est cependant facile à appliquer à d'autres bibliothèques, aussi longtemps qu'ils permettent de rasteriser, de regrouper et de tester les formes.

 var rasterGroupCounter = 0; function cacheItems() { var children = paper.project.activeLayer.children; //get all items in the project window['rasterGroup' + String(rasterGroupCounter)] = new paper.Group; var thisGroup = window['rasterGroup' + String(rasterGroupCounter)]; for (var i = 0; i < children.length; i++) { thisGroup.addChild(children[i]) // add items to group } var thisImg = thisGroup.rasterize(72); //rasterize the group thisGroup.visible = false; //hide the original `vector` group. //attach click handler on image thisImg.attach('mousedown', function(event) { if (thisGroup.contains(event.point)) { //hit test with hidden vector group thisImg.remove(); thisGroup.visible = true; } }); rasterGroupCounter++ }