ImageSharp一个专注于NetCore平台图像处理的开源项目

简介: 今天大家分享的是一个专注于NetCore平台图像处理的开源项目,老实说为这篇文章取名字想了5分钟,可能是词穷亦或是想更好的表达出这款开源项目的作用;这个项目在图像处理方面有很多功能,如:缩放,裁剪,绘画,组合图片等;今天主要讲的是用她怎么来绘图和生成验证码的实际例子。

今天大家分享的是一个专注于NetCore平台图像处理的开源项目,老实说为这篇文章取名字想了5分钟,可能是词穷亦或是想更好的表达出这款开源项目的作用;这个项目在图像处理方面有很多功能,如:缩放,裁剪,绘画,组合图片等;今天主要讲的是用她怎么来绘图和生成验证码的实际例子。

  • 简单介绍ImageSharp
  • 试试画两条线(实线和虚线)
  • 生成个缩略图
  • 在图片上画字
  • 制作一个验证码图片
  • 结合RazorPage模板,展示验证码图片

简单介绍ImageSharp

ImageSharp是对NetCore平台扩展的一个图像处理方案,在写下本文为止它最新的nuget下载量为4,034次,作者团队最近一个月刚更新的包;没错这里说最新是因为她前身和之前的版本都很受欢迎下载量也超高;她的git项目地址:https://github.com/SixLabors/ImageSharp。如果您的项目和我一样是2.0版本(2.0以前的略过),那么直接可以通过vs的nuget控制台下载对应的包,注意绘图的话需要分别下载如下两个包:

 Install-Package SixLabors.ImageSharp -Version 1.0.0-beta0001 

 Install-Package SixLabors.ImageSharp.Drawing -Version 1.0.0-beta0001 

ImageSharp用法有朋友之前写过,不过都主要针对于之前的版本,本章主要用到的都是最新的,有部分写法可能不相同。

试试画两条线(实线和虚线)

这里将用她来画两条直线并保存成图片,主要起到一个介绍作用,先来看实线如下代码:

var path = @"D:\F\学习\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\images";
            //默认实线
            using (Image<Rgba32> image = new Image<Rgba32>(500, 500))   //画布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //画布背景
                            DrawLines(
                            Rgba32.HotPink, //字体颜色
                            5,   //字体大小
                            new SixLabors.Primitives.PointF[]{
                                    new Vector2(10, 10),
                                    new Vector2(200, 150),
                                    new Vector2(50, 300)
                            } //两点一线坐标
                        )
                    );

                image.Save($"{path}/1.png"); //保存
            }

 总要步骤我都备注上文字了,这里主要通过两点一线来绘制图形,Vector2对象值得注意就是C#二维坐标(x,y)对象,其实除了Vector2还有Vector3(三维坐标)等,这对于做u3d的朋友来说不会陌生,老实说这个也是我在接触u3d时候才知道有这个类的。下面来看效果图:

由两个两点一线构造的一个角,下面来看下虚线绘制:

//虚线
            using (Image<Rgba32> image = new Image<Rgba32>(500, 500))   //画布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //画布背景
                            DrawLines(
                            Pens.Dash(Rgba32.HotPink, 5),   //字体大小
                            new SixLabors.Primitives.PointF[]{
                                    new Vector2(10, 10),
                                    new Vector2(200, 150),
                                    new Vector2(50, 300)
                            } //两点一线坐标
                        )
                    );

                image.Save($"{path}/2.png"); //保存
            }

步骤都差不多,只是调用了DrawLines的扩展方法而已,其他线条例子就不多说了各位自行实验。

生成个缩略图和在图片上画字

对于图片类型的网站来说缩略图是常见的,这里用ImageSharp生成缩略图很简单,本实例用8.png做样本来生成缩略图8-1.png,直接看例子如下是netstandard 1.3+的例子:

 //缩略图
            using (Image<Rgba32> image = Image.Load($"{path}/8.png"))
            {
                image.Mutate(x => x
                     .Resize(image.Width / 2, image.Height / 2)
                     );
                image.Save($"{path}/8-1.png");
            }

为了更好的对比缩略图和原图的区别这里对接拿两图的属性做对比如:

能很好的看出缩略图文件大小和像素都减半了,实际缩略的时候不一定减半,这全由参数控制Resize(width,height);

画字:在图片上画我们想要的字,其实类似于水印的一种需求,下面是在图片上画字的代码:

//画字 
            var install_Family = new FontCollection().Install(
                System.IO.Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/bak", "STKAITI.TTF")
                //@"C:\Windows\Fonts\STKAITI.TTF"   //字体文件
                );
            var font = new Font(install_Family, 50);  //字体
            using (Image<Rgba32> image = Image.Load($"{path}/8.png"))
            {
                image.Mutate(x => x
                     .DrawText(
                        "你们好,我是神牛",   //文字内容
                         font,
                         Rgba32.HotPink,
                         new Vector2(50, 150),
                         TextGraphicsOptions.Default)
                     );
                image.Save($"{path}/8-2.png");
            }

这里用ImageSharp在图片上画字的时候需要注意:字体,因为windows系统自带了字体问题这里以STKAITI.TTF字体文件为例,它存储于 C:\Windows\Fonts\STKAITI.TTF 目录,当然您可以直接把它拷贝到我们项目中如下我这里的例子一样做法(这里只测试了windows下可用,尚未测试linux下直接使用该字体文件是否可行);

制作一个验证码图片

下面我们将用她来画一个验证码类型的图片,通常验证码都有一些点和线来干扰,上面已经有画线例子了,这里展示怎么画点:

//画点(规则的点,其他的各位自行写算法) 
            var dianWith = 1; //点宽度
            var xx = 300;  //图片宽度
            var yy = 200;  //图片高度

            var xx_space = 10;  //点与点之间x坐标间隔
            var yy_space = 5;    //y坐标间隔

            var listPath = new List<IPath>();
            for (int i = 0; i < xx / xx_space; i++)
            {
                for (int j = 0; j < yy / yy_space; j++)
                {
                    var position = new Vector2(i * xx_space, j * yy_space);
                    var linerLine = new LinearLineSegment(position, position);
                    var shapesPath = new SixLabors.Shapes.Path(linerLine);
                    listPath.Add(shapesPath);
                }
            }

            using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //画布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //画布背景
                            Draw(
                            Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                            new SixLabors.Shapes.PathCollection(listPath)  //坐标集合
                        )
                    );
                image.Save($"{path}/9.png"); //保存
            }

这里直接利用IImageProcessingContext<TPixel>扩展方法Draw来绘制有规则的点,如图所示:

比较单调,或许您们能做的更好看些;下面来做验证码图片,主要由:画点+画字=验证码图片,这里我封装了一个方法直接生成验证码图片:

/// <summary>
        /// 画点+画字=验证码图片  
        /// </summary>
        /// <param name="content">验证码</param>
        /// <param name="outImgPath">输出图片路径</param>
        /// <param name="fontFilePath">字体文件</param>
        /// <param name="x">图片宽度</param>
        /// <param name="y">图片高度</param>
        public void GetValidCode(
                    string content = "我是神牛",
                    string outImgPath = "D:/F/学习/vs2017/netcore/Study.AspNetCore/WebApp02-1/wwwroot/images/10.png",
                    string fontFilePath = @"D:\F\学习\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\bak\STKAITI.TTF",
                    int xx = 150, int yy = 25)
        {
            var dianWith = 1; //点宽度
            var xx_space = 10;  //点与点之间x坐标间隔
            var yy_space = 5;    //y坐标间隔
            var wenZiLen = content.Length;  //文字长度
            var maxX = xx / wenZiLen; //每个文字最大x宽度
            var prevWenZiX = 0; //前面一个文字的x坐标
            var size = 16;//字体大小

            //字体
            var install_Family = new FontCollection().Install(
              fontFilePath
              //@"C:\Windows\Fonts\STKAITI.TTF"   //windows系统下字体文件
              );
            var font = new Font(install_Family, size);  //字体

            //点坐标
            var listPath = new List<IPath>();
            for (int i = 0; i < xx / xx_space; i++)
            {
                for (int j = 0; j < yy / yy_space; j++)
                {
                    var position = new Vector2(i * xx_space, j * yy_space);
                    var linerLine = new LinearLineSegment(position, position);
                    var shapesPath = new SixLabors.Shapes.Path(linerLine);
                    listPath.Add(shapesPath);
                }
            }

            //画图
            using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //画布大小
            {
                image.Mutate(x =>
                {
                    //画点
                    var imgProc = x.BackgroundColor(Rgba32.WhiteSmoke).   //画布背景
                              Draw(
                              Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                              new SixLabors.Shapes.PathCollection(listPath)  //坐标集合
                          );

                    //逐个画字
                    for (int i = 0; i < wenZiLen; i++)
                    {
                        //当前的要输出的字
                        var nowWenZi = content.Substring(i, 1);

                        //文字坐标
                        var wenXY = new Vector2();
                        var maxXX = prevWenZiX + (maxX - size);
                        wenXY.X = new Random().Next(prevWenZiX, maxXX);
                        wenXY.Y = new Random().Next(0, yy - size);

                        prevWenZiX = Convert.ToInt32(Math.Floor(wenXY.X)) + size;

                        //画字
                        imgProc.DrawText(
                           nowWenZi,   //文字内容
                           font,
                           i % 2 > 0 ? Rgba32.HotPink : Rgba32.Red,
                           wenXY,
                           TextGraphicsOptions.Default);
                    }
                });
                //保存到图片
                image.Save(outImgPath);
            }
        }

通过简单的调用 GetValidCode("我是神牛");return Page(); 能得到如图验证码图片的效果:

文字看起来好像在点的前面,不过没关系只需要把画点和画字的先后顺序修改下就行了,这里不贴图了;

结合RazorPage模板,展示验证码图片

上面一节是生成了验证码图片,当然实际场景中我们是不需要生成验证码物理图片的,只需要返回一个流或base64等方式输出到web界面上就行了,我们可以来看看 Image<TPixel> 保存时候的扩展方法:

        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 参数:
        //   source:
        //     The source image
        //
        //   filePath:
        //     The file path to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void Save<TPixel>(this Image<TPixel> source, string filePath) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 参数:
        //   source:
        //     The source image
        //
        //   filePath:
        //     The file path to save the image to.
        //
        //   encoder:
        //     The encoder to save the image with.
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the encoder is null.
        public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 参数:
        //   source:
        //     The source image
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   format:
        //     The format to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the bmp format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the bmp format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The encoder to save the image with.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the gif format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the gif format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the jpeg format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the jpeg format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the png format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the png format.
        //
        // 参数:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 类型参数:
        //   TPixel:
        //     The pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 参数:
        //   source:
        //     The source image
        //
        //   buffer:
        //     The buffer to save the raw pixel data to.
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 参数:
        //   source:
        //     The source image
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 返回结果:
        //     A copy of the pixel data as bytes from this frame.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 参数:
        //   source:
        //     The source image
        //
        //   buffer:
        //     The buffer to save the raw pixel data to.
        //
        // 类型参数:
        //   TPixel:
        //     The Pixel format.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SavePixelData<TPixel>(this Image<TPixel> source, byte[] buffer) where TPixel : struct, IPixel<TPixel>;
View Code

好吧有点多,我们只需要明白她能转base64,stream,保存为图片等就行了;这里我们将用到 SaveAsPng(Stream) 方法,然后获取他的byte[],如下代码:

/// <summary>
        /// 画点+画字=验证码byte[]
        /// </summary>
        /// <param name="content">验证码</param>
        /// <param name="outImgPath">输出图片路径</param>
        /// <param name="fontFilePath">字体文件</param>
        /// <param name="x">图片宽度</param>
        /// <param name="y">图片高度</param>
        public byte[] GetValidCodeByte(
                    string content = "我是神牛",
                    string fontFilePath = @"D:\F\学习\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\bak\STKAITI.TTF",
                    int xx = 150, int yy = 25)
        {
            var bb = default(byte[]);
            try
            {
                var dianWith = 1; //点宽度
                var xx_space = 10;  //点与点之间x坐标间隔
                var yy_space = 5;    //y坐标间隔
                var wenZiLen = content.Length;  //文字长度
                var maxX = xx / wenZiLen; //每个文字最大x宽度
                var prevWenZiX = 0; //前面一个文字的x坐标
                var size = 16;//字体大小

                //字体
                var install_Family = new FontCollection().Install(
                  fontFilePath
                  //@"C:\Windows\Fonts\STKAITI.TTF"   //windows系统下字体文件
                  );
                var font = new Font(install_Family, size);  //字体

                //点坐标
                var listPath = new List<IPath>();
                for (int i = 0; i < xx / xx_space; i++)
                {
                    for (int j = 0; j < yy / yy_space; j++)
                    {
                        var position = new Vector2(i * xx_space, j * yy_space);
                        var linerLine = new LinearLineSegment(position, position);
                        var shapesPath = new SixLabors.Shapes.Path(linerLine);
                        listPath.Add(shapesPath);
                    }
                }

                //画图
                using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //画布大小
                {
                    image.Mutate(x =>
                    {
                        var imgProc = x;

                        //逐个画字
                        for (int i = 0; i < wenZiLen; i++)
                        {
                            //当前的要输出的字
                            var nowWenZi = content.Substring(i, 1);

                            //文字坐标
                            var wenXY = new Vector2();
                            var maxXX = prevWenZiX + (maxX - size);
                            wenXY.X = new Random().Next(prevWenZiX, maxXX);
                            wenXY.Y = new Random().Next(0, yy - size);

                            prevWenZiX = Convert.ToInt32(Math.Floor(wenXY.X)) + size;

                            //画字
                            imgProc.DrawText(
                                   nowWenZi,   //文字内容
                                   font,
                                   i % 2 > 0 ? Rgba32.HotPink : Rgba32.Red,
                                   wenXY,
                                   TextGraphicsOptions.Default);
                        }

                        //画点 
                        imgProc.BackgroundColor(Rgba32.WhiteSmoke).   //画布背景
                                     Draw(
                                     Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                                     new SixLabors.Shapes.PathCollection(listPath)  //坐标集合
                                 );
                    });
                    using (MemoryStream stream = new MemoryStream())
                    {
                        image.SaveAsPng(stream);
                        bb = stream.GetBuffer();
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return bb;
        }

该方法返回了一个byte[]数组,然后通过HttpGet方式请求Razor接口,前端就能够获取到这个验证码图片byte[]了;

/// <summary>
        /// Get获取验证码图片byte[]
        /// </summary>
        /// <returns></returns>
        public FileResult OnGetValidCode()
        {
            var codebb = GetValidCodeByte(DateTime.Now.ToString("mmssfff"));
            return File(codebb, "image/png");
        }

我们通过get请求获取验证码: http://localhost:1120/login?handler=ValidCode ,然后得到如图效果:

本篇内容到此就结束了,如果对您有好的帮助,不妨点个“赞”;一起努力推动NetCore发展吧,谢谢。

目录
相关文章
|
Kubernetes 监控 持续交付
10大开源工具,每个开发者都应该知道
10大开源工具,每个开发者都应该知道
167 0
|
26天前
|
前端开发 Android开发 开发者
移动应用的构建之路:从开发到部署
【10月更文挑战第32天】本文将深入探讨移动应用的开发和部署,包括移动操作系统的选择、应用开发的生命周期、以及如何将应用有效地推向市场。我们将通过实际代码示例和清晰的步骤指导,帮助读者理解移动应用开发的全过程。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。让我们一起探索移动应用的世界,发现它的魅力和挑战吧!
|
2月前
|
数据可视化 小程序 JavaScript
UniApp可视化开发教程 | 学习如何低代码构建跨平台应用
UniApp可视化开发教程 | 学习如何低代码构建跨平台应用
92 0
|
4月前
|
安全 项目管理 数据库
"揭开Dify社区版神秘面纱:一键部署,体验开源项目管理的革命性突破!"
【8月更文挑战第20天】Dify社区版是一款开源项目管理工具,集成任务跟踪、文档协作等功能,助力团队高效协作。本文引导快速部署体验。需Linux服务器,安装Docker及Docker Compose,并能访问GitHub。从GitHub克隆源码,配置`docker-compose.yml`如数据库设置,运行`docker-compose up -d`启动服务。通过`http://&lt;服务器IP&gt;`访问Web界面,建议配置HTTPS增强安全。定期`git pull`及`docker-compose`命令实现维护升级。Dify以其实用性和灵活性,正成为项目管理领域的新兴力量。
635 1
|
4月前
|
监控 Linux 数据库连接
手把手教你从本地到云端:全面解析Blazor应用的部署流程与最佳实践,助你轻松掌握发布Blazor WebAssembly应用到Azure的每一个细节
【8月更文挑战第31天】本文详细介绍了将 Blazor 应用从本地部署到 Azure 的全过程。首先确保已在 Visual Studio 中创建 Blazor WebAssembly 应用,接着清理项目并配置发布选项。然后在 Azure 中创建 App Service 并完成应用部署。最后,配置环境变量、SSL 和监控,确保应用稳定运行。附带示例代码,展示如何加载和使用 Azure 环境变量。通过最佳实践指导,帮助你顺利完成 Blazor 应用的云端部署。
94 0
|
5月前
|
数据采集 开发工具 Android开发
构建高效移动应用:从开发到部署的全面指南构建高效Python爬虫的实战指南
【7月更文挑战第31天】在数字时代,移动应用已成为我们日常生活和工作不可或缺的一部分。本文将引导读者穿越移动应用开发的迷宫,探索如何从零开始构建一个高效的移动应用。我们将深入讨论移动操作系统的选择、开发工具的应用、以及实际编码过程中的最佳实践。通过本文,你不仅能够获得理论知识,还将通过代码示例加深理解,最终能够独立完成一个移动应用的构建和部署。
67 2
|
5月前
|
SQL 监控 前端开发
AI+若依框架(低代码开发)
AI+若依框架(低代码开发)
393 1
|
6月前
|
移动开发 Dart 前端开发
探秘移动开发新纪元:Flutter框架的跨平台魅力
Flutter,谷歌的开源UI工具包,不仅革新前端开发,其跨平台特性延伸至后端,通过Dart语言统一开发流程,提升效率与一致性。使用Aqueduct框架,开发者可构建REST API,如创建新项目、定义数据模型和控制器,实现前后端同栈开发。Flutter与Dart的结合正重塑移动开发,开启全栈新纪元。
104 2
|
6月前
|
分布式计算 算法 Java
imageJ二次开发之旅 – imageJ开源项目介绍
imageJ二次开发之旅 – imageJ开源项目介绍
166 2
|
7月前
|
Rust 前端开发 安全
【专栏】WebAssembly将支持更多语言,结合低代码平台
【4月更文挑战第27天】WebAssembly是种虚拟机格式,用于在浏览器中运行编译后的C/C++、Rust等语言代码,提供高性能、高可移植性和安全性。其优势在于更快的执行速度、跨平台兼容及安全的沙箱环境。广泛应用在游戏开发、图形处理、计算机视觉等领域。未来,WebAssembly将支持更多语言,结合低代码平台,优化开发流程,同时应对优化编译和安全性的挑战,引领高性能网络应用新时代。
84 2