[JAVA] J'ai essayé de dessiner une animation avec l'API Blazor + canvas

introduction

J'ai créé une application Web de dessin simple qui peut dessiner des animations pour l'apprentissage comme Blazor. Je parle simplement d'utiliser Blazor, et j'écris TypeScript plus souvent que C #.

Demo(Firebase) Source(GitHub)

blazorcanvas2.gif J'aimerais pouvoir dessiner un anime comme un mystérieux Ani-dan, mais pour moi, cligner des yeux est la limite.

Actuellement, ce n'est que 8 images par seconde (ce qu'on appelle la frappe à trois images), mais nous pourrons éventuellement changer la fréquence d'images. Il est facile de changer simplement la vitesse, mais comme 24 images (24 ips: fréquence d'images générale de l'animation japonaise) ne sont généralement pas dessinées pendant 1 seconde de lecture d'animation, il est nécessaire de traiter l'image vide.

environnement

Blazor WebAssembly 3.2.1 + .NET Standard 2.1 Microsoft.TypeScript.MSBuild 4.0.3 Firebase

D'autres, tels que Bootstrap inclus dans le modèle Blazor.

À propos de la mise en œuvre

Tout en disant Blazor, tout ce que Blazor fait est de contrôler l'interface utilisateur.

Comment dessiner une ligne

Lorsque je cherchais une méthode d'implémentation pour dessiner une ligne sur Canvas, il y avait de nombreux exemples d'utilisation de la méthode lineTo (), mais quand je déplaçais le stylo rapidement, cela devenait parfois une méthode de dessin étrange, donc le stylo Le cercle est placé en fonction de l'orbite. Cependant, si vous essayez de dessiner rapidement tel quel, la ligne sera coupée, j'essaie donc de combler le vide avec une petite quantité de changement, comme indiqué ci-dessous. Cependant, maintenant qu'il s'agit d'un complément linéaire, je voudrais envisager une forme légèrement plus courbe. Je pense que j'ai besoin de revoir les mathématiques.

    private prevX :number;
    private prevY :number;
    public drawLineWithPen(x:number, y:number, isDrawing:boolean){
        let scaledX = x/this.scaleRate;
        let scaledY = y/this.scaleRate;
        if(!isDrawing) {
            this.context.beginPath();
            this.context.moveTo(scaledX,scaledY);
        } else {
            //Numéro de division
            let div = 200;
            let dx = (scaledX - this.prevX) / div;
            let dy = (scaledY - this.prevY) / div;
            let r = this.context.lineWidth/2;
            for(let i = 0; i<=div; i++){
                let x = this.prevX + dx*i;
                let y = this.prevY + dy*i;
                this.context.beginPath();
                this.context.moveTo(x,y);
                this.context.arc(x,y, r, 0, 2 * Math.PI, false);
                this.context.stroke();
                this.context.fill();
                this.context.closePath();
            } 
        }
        this.prevX = scaledX;
        this.prevY = scaledY;
    }

Concernant l'insertion et le retrait du stylo, je pense à une forme qui change la taille et la transparence du cercle avec le décalage horaire comme paramètre.

Comment afficher la peau d'oignon

La peau d'oignon est une fonction qui affiche les cadres avant et après le cadre que vous modifiez dans une couleur spécifique. Cette fois, le cadre arrière est affiché en rose et le cadre avant est affiché en bleu clair.

Je fais une copie du tableau ImageData que j'ai enregistré à l'avance, remplace les couleurs et définit la transparence. Après cela, après avoir créé les données Bitmap, elles sont écrites sur le canevas pour afficher la peau d'oignon en utilisant la méthode de drawImage (). J'aimerais pouvoir écrire directement sur le canevas avec ʻImageData, mais putImageData () `remplace tout ce qui se trouve dans le canevas, donc cela ressemble à ceci.

private setOnionSkinsInternal(start:number, end:number, color:Color, frames: ImageData[], isPrev:boolean){
    let startNum = Math.max(start, 0);
    let endNum = Math.min(end,frames.length);
    for(let i= startNum; i< endNum; i++) {
        let imageData = new ImageData(frames[i].data.slice(),frames[i].width,frames[i].height);
        for(let j=0;j<imageData.data.length; j+=4) {
            imageData.data[j] = color.r;
            imageData.data[j+1] = color.g;
            imageData.data[j+2] = color.b;
            //Les images éloignées de l'image actuelle ont une transparence plus forte et un affichage plus clair
            if(isPrev)
                imageData.data[j+3] = imageData.data[j+3] * (i+1) / (endNum+1);
            else
                imageData.data[j+3] = imageData.data[j+3] * (startNum+1) / (i+1);          
        }
        window.createImageBitmap(imageData).then(
            (img) => {
                // scale()Puisque l'image est encore réduite par le grossissement défini, divisez par le grossissement?
                this.context.drawImage(img,0,0,this.width/this.scaleRate,this.height/this.scaleRate);
            }
        ).catch(() => {
            console.log(`${i} Error`)});
    }
}

Il y a 921 600 boucles ou plus par feuille, donc si vous essayez d'augmenter la taille et d'afficher environ 10 feuilles, ce sera assez lourd. Je veux penser à une meilleure façon. J'ai pensé à ajouter une toile pour le nombre de peaux d'oignon, mais je ne l'ai pas essayé.

Gomme et composition des calques

https://hai3.net/blog/html5-canvas-eraser/ J'ai évoqué cette méthode. Merci beaucoup.

CanvasRenderingContext2D.globalCompositeOperation = "destination-out"; Lors du dessin avec la gomme, c'était OK si cela était défini.

https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation Pour autant que je lis MDN, je pense que la composition du calque et l'aspect aquarelle de la pointe du stylo peuvent être obtenus en utilisant cela.

Actuellement, le seul moyen de passer d'une gomme à un stylo est de changer la couleur ou la taille de la pointe du stylo, je voudrais donc créer une méthode de commutation appropriée en forme de bouton radio.

undo/redo Gardez juste ʻImageData [] `pour le stockage de l'historique et pop ou push chaque fois que la méthode undo / redo est appelée. J'ai pensé à sauvegarder l'historique du côté TypeScript, mais je veux le contrôler du côté de l'interface utilisateur, donc je laisse le timing pour l'appeler du côté Blazor. Actuellement, ~~ est facile à implémenter ~~ Il a un historique pour chaque image, mais il semble que la manière de conserver l'historique changera au moment de l'implémentation de la suppression de l'image.

public undo(){
    if(this.currentFrame.prevHistory.length > 0) {
        let prev = this.currentFrame.prevHistory.pop() ?? new ImageData(this.width,this.height) ;
        this.currentFrame.nextHistory.push(this.getImageData());
        this.putImageData(prev);      
    }
}

public redo(){
    if(this.currentFrame.nextHistory.length > 0){
        let next = this.currentFrame.nextHistory.pop() ?? new ImageData(this.width,this.height);
        this.currentFrame.prevHistory.push(this.getImageData());
        this.putImageData(next);
    }
}

public saveHistory(){
    this.currentFrame.prevHistory.push(this.getImageData());
    //Lorsqu'un nouvel historique est ajouté, l'historique de transfert est supprimé.
    this.currentFrame.nextHistory = [];
}

priorité de l'élément de canevas

<canvas id="1"></canvas>
<canvas id="2"></canvas>
<canvas id="3"></canvas>

Si vous récupérez un événement avec ʻid = "1" `, vous devez l'amener en bas pour récupérer l'événement.

TypeScript + dotnet CLI C'était très facile car le fichier .ts était également compilé avec le code C # lorsque dotnet run était fait avec la commande dotnet add package Microsoft.TypeScript.MSBuild et les paramètres du fichier json. J'ai pensé à écrire en F # en utilisant Fable, mais j'ai abandonné car cela semble être strict en raison des spécifications d'interopérabilité JavaScript où le nom de la méthode doit être spécifié à partir de la méthode de génération de nom de méthode.

Fonctionnalités à ajouter à l'avenir

J'ai écrit dans le READ ME de GitHub que c'était un gâchis, mais comme le problème du confort de dessin est grand, je veux résoudre le problème que les lignes sont raides et que l'entrée et la sortie sont complètes. De plus, si vous utilisez l'API Web Storage, il semble que vous puissiez enregistrer les valeurs de réglage de la couleur et de la taille du stylet, je voudrais donc prendre en charge cela également.

La prise en charge des tablettes et des smartphones sera longtemps plus tard.

À propos de moi

Neat semble avoir été impliqué dans la création de système à l'aide de l'application Windows Form. Je n'ai qu'une petite compréhension de C # et C, et je n'ai aucune expérience avec TypeScript.

Intention de faire

Je ne peux pas facilement dessiner et déplacer des animations avec certains logiciels de dessin, c'est difficile à utiliser même si je fais des animations, il n'y a pas d'effet d'écran, et je pense qu'il ne semble pas y avoir d'application de dessin (Web) qui s'est tournée de manière inattendue dans cette direction De. De plus, il n'y a pas d'opération de base de données ou de fonction de connexion, mais à part des problèmes tels que l'opportunité et la qualité, il s'agit de le montrer à quelqu'un quelque part.

À l'origine, j'essayais l'API Canvas et je ne pensais pas à la fonction d'animation, mais au fur et à mesure que je la fabriquais, je voulais ajouter la fonction, et maintenant je le suis.

en conclusion

Il y a encore de nombreuses fonctionnalités que je souhaite implémenter et des points que je souhaite améliorer, alors j'aimerais continuer tant que ma motivation continue. TypeScript a été développé par Microsoft, et c'était beaucoup plus facile car je pouvais voir des erreurs du côté TypeScript au moment de la compilation car il y avait une partie similaire à C # et il y avait une combinaison avec dotnet CLI. Blazor peut également toucher l'API du navigateur par lui-même, mais comme cela ne peut être fait que via IJS Runtime, il semble être strict de n'utiliser que Blazor (C #).

Recommended Posts

J'ai essayé de dessiner une animation avec l'API Blazor + canvas
J'ai essayé d'interagir avec Java
J'ai essayé de lier le chat avec le serveur de Minecraft avec l'API Discord
J'ai essayé de démarrer avec Web Assembly
J'ai essayé de résumer l'API Stream
J'ai essayé de créer une API Web qui se connecte à DB avec Quarkus
J'ai essayé de vérifier AdoptOpenJDK 11 (11.0.2) avec l'image Docker
J'ai essayé de faire une authentification de base avec Java
J'ai essayé de gérer les informations de connexion avec JMX
J'ai essayé de casser le bloc avec java (1)
J'ai essayé ce que je voulais essayer avec Stream doucement.
J'ai essayé d'implémenter le téléchargement de fichiers avec Spring MVC
J'ai essayé de lire et de sortir CSV avec Outsystems
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
[Java 11] J'ai essayé d'exécuter Java sans compiler avec javac
J'ai essayé de démarrer avec Spring Data JPA
J'ai essayé d'implémenter Sterling Sort avec Java Collector
[Java] J'ai essayé de mettre en œuvre la recherche de produits de l'API Yahoo
J'ai essayé de vérifier le fonctionnement de la requête http (Put) avec Talented API Tester
J'ai essayé DI avec Ruby
J'ai essayé UPSERT avec PostgreSQL.
J'ai essayé BIND avec Docker
J'ai essayé de vérifier yum-cron
J'ai essayé de créer un environnement de développement java8 avec Chocolatey
J'ai essayé de moderniser une application Java EE avec OpenShift.
J'ai essayé d'augmenter la vitesse de traitement avec l'ingénierie spirituelle
[Rails] J'ai essayé de créer une mini application avec FullCalendar
J'ai essayé de faire de Ben figure une animation GIF facile à comprendre
[Rails] J'ai essayé d'implémenter le traitement par lots avec la tâche Rake
Ce à quoi j'étais accro avec l'API REST Redmine
J'ai essayé de créer un environnement de développement padrino avec Docker
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
J'ai essayé de pouvoir passer plusieurs objets avec Ractor
J'ai essayé de créer un serveur API avec Go (Echo) x MySQL x Docker x Clean Architecture
J'ai essayé de mâcher C # (indexeur)
J'ai essayé d'utiliser JOOQ avec Gradle
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
J'ai essayé de me connecter à MySQL en utilisant le modèle JDBC avec Spring MVC
J'ai essayé de résumer le support d'iOS 14
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
J'ai essayé de créer un environnement de développement http2 avec Eclipse + Tomcat
J'ai essayé d'implémenter un mappage OU flexible avec MyBatis Dynamic SQL
J'ai essayé la communication UDP avec Java
J'ai essayé d'expliquer la méthode
J'ai essayé de réimplémenter Ruby's Float (arg, exception: true) avec builtin
J'ai essayé d'utiliser l'API Java8 Stream
J'ai essayé de créer une application Android avec MVC maintenant (Java)
J'ai essayé de vérifier le fonctionnement du serveur gRPC avec grpcurl
J'ai essayé GraphQL avec Spring Boot
J'ai essayé de résumer l'apprentissage Java (1)
J'ai essayé Flyway avec Spring Boot
J'ai essayé de résumer Java 8 maintenant
C # (polymorphisme: polymorphisme)
J'ai essayé de personnaliser Slim avec Scaffold
J'ai essayé d'expliquer Active Hash