unity UGUI中获取点击位置处的URL链接

需求是,我们在一个text组件中像写网页那样写入链接,然后点击这个链接,就能访问配置的网页啥的。比如:

<a href="hello">链接文本</a></summary>

最终的效果如下:

图中,image区域就是各个链接的点击范围。原理是获取text中,每个字符的位置,然后算出每个链接对应的点击区域,最后返回鼠标点到的那个区域的链接。代码比较简单,就直接写点注释看吧。实现是继承了text组件,当然写成静态方法传入text来计算也可以。

比较一下网上搜到的其他方案,这个方法不用重载mesh,效率应该是比较高的。

#define TEST_CheckClickURL
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;

public class TestClickURL : Text
{
    public Button button;
    public void OnButtonClick()
    {
        Debug.Log(CheckClickURL()?.url);
    }

    // 定义返回的结果
    public class CheckClickURLResult
    {
        public string url;
        public string text;
        public Rect rect;
        public CheckClickURLResult(string url, string text, Rect rect)
        {
            this.url = url;
            this.text = text;
            this.rect = rect;
        }
    }
    private static Regex hrefRegex =
        new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)",
            RegexOptions.Singleline);
    /// <summary> 计算点击到的URL文本内容,返回网址
    /// 格式如下:<a href="hello">链接文本</a></summary>
    public CheckClickURLResult CheckClickURL()
    {
        Profiler.BeginSample("CheckClickURL");
        if (hrefRegex == null)
            hrefRegex = new Regex(
                @"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
        InitDebugGOList();

        // 将点击位置从屏幕坐标转为本地坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            this.rectTransform, Input.mousePosition,
            null, out var mouseLocalPosition);
        //注意使用UI相机

        // 获取生成的文本数据。
        // characters 保存了每个字符左上角的位置。
        // lines 保存了每行开始字符ID,和行高。
        var generator = cachedTextGenerator;
        var charList = generator.characters;
        var lineList = generator.lines;
        var textStr = text;

        // 正则表达式查找链接文本
        var matchs = hrefRegex.Matches(textStr);
        foreach (Match match in matchs)
        {
            var urlGroup = match.Groups[1];
            var textGroup = match.Groups[0];
            var textStartIndex = textGroup.Index;
            var textEndIndex = textGroup.Index + textGroup.Length;
            // 我们的字符可能是换行的,所以要按行分割。
            // 倒着遍历就很容易获取每行开始和结束位置。
            var lineEndIndex = charList.Count - 1;
            for (int i = lineList.Count - 1; i >= 0; i--)
            {
                var lineStartIndex = lineList[i].startCharIdx;
                // 处理换行后的截取
                var realStart = Mathf.Max(lineStartIndex, textStartIndex);
                var realEnd = Mathf.Min(lineEndIndex, textEndIndex);
                // 本行没有链接内容的情况
                if (realStart > realEnd) continue;
                // 问题简化成单行的点击检查,提个函数继续处理。
                var result = CheckLine(realStart, realEnd, lineList[i].height, mouseLocalPosition, out var rect);
                if (result) return new CheckClickURLResult(urlGroup.Value, textGroup.Value, rect);

                //Debug.Log($"{start}/{end}");
                lineEndIndex = lineStartIndex - 1;
            }
        }
        Profiler.EndSample();
        return null;
    }

    public bool CheckLine(int start, int end, float lineHeight, Vector2 mouseLocalPosition, out Rect rect)
    {
        // 获取生成的文本数据。
        var charList = cachedTextGenerator.characters;
        var startPoint = charList[start].cursorPos;
        var endPoint = charList[end].cursorPos;

        // 直接计算出本行中链接可点击区域。
        var x = startPoint.x;
        var y = startPoint.y - lineHeight;
        var width = endPoint.x - startPoint.x;
        var height = lineHeight;
        rect = new Rect(x, y, width, height);

        var result = rect.Contains(mouseLocalPosition);
        CreateDebugImage(rect, result);
        return result;
    }

#if TEST_CheckClickURL
    // 测试用。生成空image展示出点击判定范围。
    public static List<GameObject> debugGOList;
    public void CreateDebugImage(Rect rect, bool contains)
    {
        Debug.Log($"rect={rect}");
        var go = new GameObject("DebugImage",
            typeof(RectTransform), typeof(Image));
        debugGOList.Add(go);
        var rtf = go.GetComponent<RectTransform>();
        rtf.SetParent(transform);
        rtf.pivot = Vector2.zero;
        rtf.anchorMin = Vector2.one / 2;
        rtf.anchorMax = Vector2.one / 2;
        rtf.sizeDelta = rect.size;
        rtf.localScale = Vector3.one;
        rtf.rotation = Quaternion.identity;
        rtf.anchoredPosition = rect.position - rectTransform.rect.center;
        // 点击到的那个范围展示为红色。
        if (contains)
            go.GetComponent<Image>().color = Color.red;
    }
    public void InitDebugGOList()
    {
        if (debugGOList == null)
            debugGOList = new List<GameObject>();
        debugGOList.ForEach(p => Destroy(p));
    }
#else
        public void CreateDebugImage(Rect rect, bool contains) { }
        public void InitDebugGOList() { }
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/194574.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能优化算法应用:基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜻蜓算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

Docker Compose;docker-compose;docker compose

(一) Docker Compose | 菜鸟教程 --> --> --> -->

Spine深入学习———— 渲染

数据有了之后&#xff0c;就开始渲染 渲染相关 绘制顺序 骨架的绘制顺序就是一个插槽列表&#xff0c;在插槽列表中上方的附件在下方之上绘制&#xff0c;绘制顺序可以在层级树中的骨架下查看。 基础流程 渲染实现 以下按照cocos2dx的实现来 &#xff08;cocos2dx 3.7 spin…

内部类, Comparable接口, Comparator接口, Cloneable接口 ---java

目录 一. 内部类 1.1 静态内部类 1.2 实例内部类 1.3匿名内部类 二. 接口的使用实例 2.1 Comparable接口 2.2 Comparator接口 ---比较器 2.3 Cloneable接口 深拷贝浅拷贝 一. 内部类 当一个事物的内部&#xff0c;还有一个部分需要一个完整的结构进行描述&#xff0…

机器学习——支持向量机(SVM)

1.线性支持向量机 1.1数学模型 机器学习最终都是求解目标函数的最优问题&#xff1b; 一般都是讲问题转化为最小值来求解。 数学模型获得是一个不等式约束的最小化问题&#xff0c;求解时可通过构建拉格朗日函数求解。 1.2 拉格朗日函数及对偶问题求解 1.3 SMO算法求解 SMO算…

vue中的keep-alive详解与应用场景

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-keep-alive 目录 一、Keep-alive 是什么 二、使用场景 三、原理分析 四、案例实现 activa…

虹科干货 | 适用于基于FPGA的网络设备的IEEE 1588透明时钟架构

导读&#xff1a;在基于FPGA的网络设备中&#xff0c;精确的时间同步至关重要。IEEE 1588标准定义的精确时间协议&#xff08;PTP&#xff09;为网络中的设备提供了纳秒级的时间同步。本文将介绍虹科提供的适用于基于FPGA的网络设备的IEEE 1588透明时钟&#xff08;TC&#xff…

flink源码分析之功能组件(三)-rpc组件

简介 本系列是flink源码分析的第二个系列,上一个《flink源码分析之集群与资源》分析集群与资源,本系列分析功能组件,kubeclient,rpc,心跳,高可用,slotpool,rest,metrics,future。 本文解释rpc组件,rpc组件用于个核心组件,包括作业管理器,资源管理器和任务管理器之…

uni-app 离线打包安卓Apk(小白上手)

场景&#xff1a; 在使用uni-app 开发apk时&#xff0c;使用云打包有次数限制。尤其对于测试阶段是无比难受的&#xff0c;通常是浪费打包次数进行打包或者通过usb 给测试机更新开发环境&#xff0c;但这都是无比漫长的过程 尤其有多个测试机真的是噩梦般的存在 下载离线打包示…

基于mpvue实现的cnode社区demo(附精选源码32套,涵盖商城团购等)

社区类目没有开放给个人开发者&#xff0c;所以没能上线。 预览 项目配置文件&#xff0c;更改appid {"description": "项目配置文件","setting": {"urlCheck": true,"es6": false,"postcss": false,"minif…

扫地机器人市场持续火爆,景联文科技数据采集标注方案助力扫地机器人智能化升级

随着消费者对智能家居和清洁卫生的需求增加&#xff0c;扫地机器人市场规模不断扩大。市场竞争也日益激烈&#xff0c;各品牌都在努力提升产品性能和服务质量&#xff0c;以获取更大的市场份额。 IDC的统计数据显示&#xff0c;今年双十一前两周&#xff08;2023年10月23日至20…

stream流和方法引用

1.Stream流 1.1体验Stream流【理解】 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素把集合中所有以"张"开头的元素存储到一个新的集合把"张"开头的集合中的长度为3的元素存储到一个新的集合遍历上一步得到的集…

jsoup登录日志平台后调企业微信机器人自动发送错误日志告警

一、需求&#xff1a;错误日志Top10告警发送 二、需求分解 jsoup实现登录&#xff0c;获取到cookie和token等用户鉴权信息获取接口相应的key值调用日志平台错误日志Top榜接口&#xff0c;查询到结果集调用企业微信机器人发送消息接口加上定时任务&#xff0c;可以实现定时发送…

顺子日期(14)

顺着日期 public class Main {public static void main(String[] args) {int res 0;//2022年int[] days new int[] {31,28,31,30,31,30,31,31,30,31,30,31};//31,28,31,30,31,30,31,31,30,31,30,31//一三五七八十腊//构造2022年每一天的日期yyyymmddStringBuffer date new…

用python实现kindle文件转换pdf

上一篇文章讲了下用工具转换相关的格式&#xff1a;https://blog.csdn.net/weixin_42019349/article/details/134654695 今天来分享一个python库实现上述功能&#xff0c;实现文件转换自由 ^_^ 主角就是pypandoc库 # 安装方式 pip install pypandoc# pypandoc主要有三个函数…

ModuleNotFoundError: No module named ‘mdtex2html‘ module已经安装还是报错,怎么办?

用streamlit运行ChatGLM/basic_model/web_demo.py的时候&#xff0c;出现了module not found&#xff1a; ModuleNotFoundError: No module named mdtex2html Traceback: File "/home/haiyue/.local/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script…

机器学习的复习笔记3-回归的细谈

一、回归的细分 机器学习中的回归问题是一种用于预测连续型输出变量的任务。回归问题的类型和特点如下&#xff1a; 线性回归&#xff08;Linear Regression&#xff09;&#xff1a;线性回归是回归问题中最简单的一种方法。它假设自变量与因变量之间存在线性关系&#xff0c…

普通表计读数开发思路

一、普通表计类型介绍&#x1f349; 常见的普通表计有SF6&#xff0c;压力表&#xff0c;油位表&#xff08;指针类&#xff09;等。 图1&#xff1a;( 压力表) 图2&#xff1a;&#xff08;油位表-指针类&#xff09; 图3&#xff1a;&#xff08;SF6表&#xff09; 图4:&a…

巧妙之中见真章:深入解析常用的创建型设计模式

设计模式之创建型设计模式详解 一、设计模式是什么&#xff1f;二、模板方法2.1、代码结构2.2、符合的设计原则2.3、如何扩展代码2.4、小结 三、观察者模式3.1、代码结构3.2、符合的设计原则3.3、如何扩展代码3.4、小结 四、策略模式4.1、代码结构4.2、符合的设计原则4.3、如何…

K 最近邻算法

K 最近邻算法 简单 KNN海伦约会手写数字识别KNN 算法的优缺点 K 最近邻&#xff08;K-NearestNeighbor&#xff0c;KNN&#xff09;算法&#xff0c;是 1967 年由 Cover T 和 Hart P 提出的一种用于分类与回归的方法。 基本原理&#xff1a;存在一个带标签的数据集&#xff08;…