I made a simple drawing web application that can draw animation for learning such as Blazor. I'm just talking about using Blazor, and I write TypeScript more often than C #.
I wish I could draw an anime like a mysterious Ani-dan, but for me, blinking is the limit.
Currently it is only 8fps (so-called three-frame striking), but eventually we will be able to change the frame rate. It's easy to just change the rate, but since 24 images (24fps: general frame rate of Japanese animation) are not usually drawn for 1 second of animation playback, it is necessary to deal with the empty frame.
Blazor WebAssembly 3.2.1 + .NET Standard 2.1 Microsoft.TypeScript.MSBuild 4.0.3 Firebase
Others, such as Bootstrap included in the Blazor template.
While saying Blazor, all that Blazor does is control the UI.
When I was looking for an implementation method to draw a line on Canvas, there were many examples of using the lineTo ()
method, but when I moved the pen quickly, it sometimes became a strange drawing method, so the pen's The circle is placed according to the orbit.
However, if you try to draw quickly as it is, the line will be cut, so I try to fill the gap with a small amount of change as shown below. However, now that it is a linear complement, I would like to consider a more curved shape. I think I need to review math.
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 {
//Division number
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;
}
Regarding the insertion and removal of the pen, I am thinking of changing the size and transparency of the circle using the time difference as a parameter.
Onion skin is a function to display the frames before and after the frame being edited in a specific color. This time, the rear frame is displayed in pink and the front frame is displayed in light blue.
I make a copy of the ImageData array that I have saved in advance, replace the colors, and set the transparency. After that, after creating the Bitmap data, it is written to the canvas for displaying the onion skin using the method of drawImage ()
. I wish I could write directly to the canvas with ʻImageData, but
putImageData () `replaces everything in the canvas, so it looks like this.
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;
//Frames far from the current frame have stronger transparency and lighter display
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()The image is further reduced by the magnification set in, so divide by the magnification?
this.context.drawImage(img,0,0,this.width/this.scaleRate,this.height/this.scaleRate);
}
).catch(() => {
console.log(`${i} Error`)});
}
}
There are 921,600 or more loops per sheet, so if you try to increase the size and display about 10 sheets, it will be quite heavy. I want to think of a better way. I thought about adding canvas for the number of onion skins, but I haven't tried it.
https://hai3.net/blog/html5-canvas-eraser/ I referred to this method. Thank you very much.
CanvasRenderingContext2D.globalCompositeOperation = "destination-out";
When drawing with an eraser, it was OK if I set this.
https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation As far as I read MDN, I feel that layer composition and the watercolor-likeness of the pen tip can be achieved using this.
Currently, the only way to switch from an eraser to a pen is to switch colors or change the pen tip size, so I would like to create a radio button-like switching method.
undo/redo Just have ʻImageData []` for saving history and pop or push each time the undo / redo method is called. I thought about saving the history on the TypeScript side, but I want to control it on the UI side, so I leave the timing to call it to the Blazor side. Currently, ~~ is easy to implement ~~ It has a history for each frame, but it seems that the way of holding the history will change at the timing of implementing frame deletion.
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());
//When a new history is added, the forward history is deleted.
this.currentFrame.nextHistory = [];
}
<canvas id="1"></canvas>
<canvas id="2"></canvas>
<canvas id="3"></canvas>
If the event is picked up by ʻid = "1" `, you must bring it to the bottom to pick up the event.
TypeScript + dotnet CLI
The .ts file was also compiled with the C # code when I did a dotnet run
with the dotnet add package Microsoft.TypeScript.MSBuild
command and the json file configuration, which was very easy.
I thought about writing in F # using Fable, but I gave up because it seems to be strict due to the specification of JavaScript interoperability where the method name must be specified from the method name generation method.
I wrote in the README on GitHub that it was a mess, but since the problem of drawing comfort is big, I want to solve the problem that the lines are stiff or the entry and exit are all-out. Also, if you use the Web Storage API, it seems that you can save the setting values of color and pen size, so I would like to support that as well.
Support for tablets and smartphones will be a long time later.
Neat seems to have been involved in system production using Windows Form applications. I have only a little understanding of C # and C, and I have no experience with TypeScript.
I can't easily draw and move animations with certain drawing software, it's difficult to use even if I make animations, there is no screen effect, and I think that there seems to be no drawing (Web) application that unexpectedly turned to this direction. From. Also, there is no database operation or login function, but apart from issues such as opportunity and quality, it is for showing to someone somewhere.
Originally, I was trying the Canvas API and didn't think about the animation function, but as I was making it, I wanted to add the function, and now I am.
There are still many features I want to implement and points I want to improve, so I'd like to continue as long as my motivation continues. TypeScript was developed by Microsoft, and it was a lot easier because I could see some errors on the TypeScript side at compile time because there was a part similar to C # and there was a combination with dotnet CLI. Blazor can also touch the API of the browser by itself, but since it can only be done via IJS Runtime, it seems to be strict to use only Blazor (C #).
Recommended Posts