Signature électronique Unity Courbe de Bézier enregistrer l'image

Rappel : Le code source est joint à la publication ~ Tout le monde peut apprendre les uns des autres


avant-propos

Habituellement, lorsque vous vous rendez à la banque pour gérer vos affaires, vous rencontrerez toujours des signatures électroniques et vous pourrez prouver votre identité en signant électroniquement des documents en ligne. Dans le système récemment produit, la fonction de signature électronique est également requise pour que le personnel puisse réviser et signer des documents en ligne ! Alors organisez les composants pour un accès facile à chaque fois ! Apprenons ensemble !


1. Structure des composants

  1. Structure préfabriquée
    insérez la description de l'image ici

  2. structuration du projet
    insérez la description de l'image ici

2. Réalisation de la fonction

1. Signature courbe de Bézier

  • Ajoutez signImage dans le panneau de la zone de signature, prenez la souris et placez-la sur le panneau, puis actualisez et affichez le contenu de la signature en temps réel.
  	/// <summary>
    /// 获取鼠标当前所在UI
    /// </summary>
    /// <param name="canvas"></param>
    /// <returns></returns>
    public GameObject GetOverUI(GameObject canvas)
    {
    
    
        PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
        pointerEventData.position = Input.mousePosition;
        GraphicRaycaster gr = canvas.GetComponent<GraphicRaycaster>();
        List<RaycastResult> results = new List<RaycastResult>();
        gr.Raycast(pointerEventData, results);
        if (results.Count != 0)
        {
    
    
            return results[0].gameObject;
        }
        return null;
    }
    
	void Update()
    {
    
    
        if (Input.GetMouseButton(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
        {
    
    

                IsUpdataRawImage = true;
                OnMouseMove(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                BiHua.Add(1);
        }
        if (Input.GetMouseButtonUp(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
        {
    
    
           
                OnMouseUp();
                queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
                BiHua.Add(0);
                //添加比划
                BiHuanIndex++;
                BiHuaList.Add(getTexture2d(texRender));
                print("BiHuanIndex" + BiHuanIndex + "BiHuaList" + BiHuaList.Count);
               
        //一直刷新显示
        if (IsUpdataRawImage == true)
        {
    
    
            DrawImage();
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
    
    
            Application.Quit();
        }
        if (IsRePlay == true && numA > 0)
        {
    
    
            numA--;
            print("现在的Index" + ((queue.Count - 1) - numA).ToString());
            if (BiHuaIndex > queue.Count - 2)
            {
    
    
                return;
            }
            else
            {
    
    
                AutoPlay((Vector3)queue[(queue.Count - 1) - numA], (queue.Count - 1) - numA);
            }
        }
    }
  • La méthode de dessin de base gère les courbes de dessin en obtenant le mouvement et le levage de la souris.
   /// <summary>
    /// 当鼠标移动时
    /// </summary>
    /// <param name="pos"></param>
    void OnMouseMove(Vector3 pos)
    {
    
    
        if (startPosition == Vector3.zero)
        {
    
    
            startPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
        }
        endPosition = pos;
        float distance = Vector3.Distance(startPosition, endPosition);
        brushScale = SetScale(distance);
        ThreeOrderBézierCurse(pos, distance, 4.5f);
        startPosition = endPosition;
        lastDistance = distance;
    }
 void OnMouseUp()
    {
    
    
        startPosition = Vector3.zero;
        //brushScale = 0.5f;
        a = 0;
        b = 0;
        s = 0;
    }

	// <summary>
    /// 三阶贝塞尔曲线,获取连续4个点坐标,通过调整中间2点坐标,画出部分(我使用了num/1.5实现画出部分曲线)来使曲线平滑;通过速度控制曲线宽度。
    /// </summary>
    /// <param name="pos"></param>
    /// <param name="distance"></param>
    /// <param name="targetPosOffset"></param>
    private void ThreeOrderBézierCurse(Vector3 pos, float distance, float targetPosOffset)
    {
    
    
        //记录坐标
        PositionArray1[b] = pos;
        b++;
        //记录速度
        speedArray[s] = distance;
        s++;
        if (b == 4)
        {
    
    
            Vector3 temp1 = PositionArray1[1];
            Vector3 temp2 = PositionArray1[2];

            //修改中间两点坐标
            Vector3 middle = (PositionArray1[0] + PositionArray1[2]) / 2;
            PositionArray1[1] = (PositionArray1[1] - middle) * 1.5f + middle;
            middle = (temp1 + PositionArray1[3]) / 2;
            PositionArray1[2] = (PositionArray1[2] - middle) * 2.1f + middle;

            for (int index1 = 0; index1 < num / 1.5f; index1++)
            {
    
    
                float t1 = (1.0f / num) * index1;
                Vector3 target = Mathf.Pow(1 - t1, 3) * PositionArray1[0] +
                                 3 * PositionArray1[1] * t1 * Mathf.Pow(1 - t1, 2) +
                                 3 * PositionArray1[2] * t1 * t1 * (1 - t1) + PositionArray1[3] * Mathf.Pow(t1, 3);
                //float deltaspeed = (float)(distance - lastDistance) / num;
                //获取速度差值(存在问题,参考)
                float deltaspeed = (float)(speedArray[3] - speedArray[0]) / num;
                //float randomOffset = Random.Range(-1/(speedArray[0] + (deltaspeed * index1)), 1 / (speedArray[0] + (deltaspeed * index1)));
                //模拟毛刺效果
                float randomOffset = Random.Range(-targetPosOffset, targetPosOffset);
                DrawBrush(texRender, (int)(target.x + randomOffset), (int)(target.y + randomOffset), brushTypeTexture, brushColor, SetScale(speedArray[0] + (deltaspeed * index1)));
            }

            PositionArray1[0] = temp1;
            PositionArray1[1] = temp2;
            PositionArray1[2] = PositionArray1[3];

            speedArray[0] = speedArray[1];
            speedArray[1] = speedArray[2];
            speedArray[2] = speedArray[3];
            b = 3;
            s = 3;
        }
        else
        {
    
    
            DrawBrush(texRender, (int)endPosition.x, (int)endPosition.y, brushTypeTexture,
                brushColor, brushScale);
        }

    }

2. Effacer

Lorsqu'il y a une erreur d'écriture, la signature générée sur la page doit être effacée et redessinée.

   /// <summary>
    /// 重写
    /// </summary>
    public void OnClickClear()
    {
    
    
        text.gameObject.SetActive(false);
        Clear(texRender);
        BiHuaList.Clear();
        BiHuanIndex = 0;
        Resources.UnloadUnusedAssets();
        GC.Collect();
        BiHuaIndex = 0;
        queue.Clear();
        BiHua.Clear();
        IsUpdataRawImage = true;
        startPosition = Vector3.zero;
        //brushScale = 0.5f;
        a = 0;
        b = 0;
        s = 0;
        isClear = true;
    }

3. Enregistrer

Enregistrez-le dans le répertoire spécifié sous forme d'images ou enregistrez-le dans la base de données sous forme binaire.

 /// <summary>
    /// 将书写好的效果保存成为Png
    /// </summary>
    public void Save()
    {
    
    
        string filepath = System.DateTime.Now.ToString("yyMMddHHmmss") + ".png";
        StartCoroutine(SaveRenderTextureToPNG(texRender, filePath, filepath));
    }

    /// <summary>
    /// 将RenderTexture保存成一张png图片
    /// </summary>
    /// <param name="rt"></param>
    /// <param name="contents"></param>
    /// <param name="pngName"></param>
    /// <returns></returns>
    IEnumerator SaveRenderTextureToPNG(RenderTexture rt, string contents, string pngName)
    {
    
    
        RenderTexture prev = RenderTexture.active;
        RenderTexture.active = rt;
        Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
        png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        Color[] cc = png.GetPixels();
        for (int i = 0; i < cc.Length; i++)
            if (cc[i].r == 1)
                cc[i] = Color.clear;
        png.SetPixels(cc);
        byte[] bytes = png.EncodeToPNG();
        //-------------1.可舍弃方式2,在此处上传图片至服务器存储------------
        //signPicture.sprite = ByteToSprite(bytes, 15, 15);//反显数据库取回图片
        //-------------2.此处为保存至本地-----------
        if (!Directory.Exists(contents))
            Directory.CreateDirectory(contents);
        FileStream file = File.Open(contents + "/" + pngName, FileMode.Create);
        BinaryWriter writer = new BinaryWriter(file);
        writer.Write(bytes);
        file.Close();
        Texture2D.DestroyImmediate(png);
        RenderTexture.active = prev;
        text.text = "已保存成功";
        text.gameObject.SetActive(true);
        yield return writer;
    }

PS : Dans le code actuel, l'image est stockée au format png dans le chemin Application.streamingAssetsPath + "/QianMing/" sous le projet.

4. Rejouer

Réalisez la relecture des signatures dans l'ordre d'écriture.

 /// <summary>
    /// 重放功能  
    /// </summary>
    public void RePlay()
    {
    
    
        text.gameObject.SetActive(false);
        BiHuaIndex = 0;
        Clear(texRender);
        Proxy = queue[0];
        IsRePlay = true;
        numA = queue.Count;
        BiHuanIndex = 0;
        BiHuaList.Clear();
        startPosition = Vector3.zero;
        a = 0;
        b = 0;
        s = 0;

    }


3. Description du scénario

La méthode fonctionnelle est classée dans SignPanel.cs, lie le script à la caméra et introduit un contenu connexe tel que le matériau du pinceau.
insérez la description de l'image ici

Résumer

La structure des composants est simple, la hiérarchie est claire et facile à comprendre. L'interface utilisateur et la taille peuvent être modifiées pour différentes occasions. Résumé personnel et induction, facile à utiliser. Évitez de réinventer la roue~~~

Téléchargement du composant CSDN : https://download.csdn.net/download/u014641682/87577410

Je suppose que tu aimes

Origine blog.csdn.net/u014641682/article/details/129538212
conseillé
Classement