react-native中使用webView全局添加水印

功能需求说明

需求说明:客户那边要求,给目前正在使用的安卓app添加水印,水印上标注上登录人,登录人电话号码和登录日期。

因为公司原因,就不放效果图了,大概就是水印效果图,下面大概讲述一下实现方案。

先后查询了react-native-image-marker(一直安装不上,我的react-native版本为0.68.2)这个仓库和react-native-watermarker(库最后一次更新7年前,然后放弃)

思考实现

根据同事的提醒,表示可以参考一下web端实现添加水印的办法。当时考虑到两个小问题,一个是如何给全局加水印,另外一个则是水印肯定是要加在最上面的,那么点击事件如何穿透呢。

第一个问题很好解决,如何给全局加水印,react-native中也有关于路由的地方,就是使用react-navigation这个库来实现,我们可以在这个地方给所有路由的组件包装一下,然后添加一下自己编写的waterMark组件。

第二个问题我是在网上搜才发现有一个属性是"ponitEvents"属性,这个属性大概就是用来做点击事件穿透的。

好了,两个问题都解决了,开始下一步,进行编写代码

代码实现

我们需要改动的地方有两个地方,一个是找到需要统一修改路由的地方,第二个就是编写一个水印的组件

修改路由的地方大概就是你遍历Stack.Screen的地方,代码大概可以这么写(虽然会出现一些问题),可能会有一个props传递异常的一个黄色警告,但对于功能实现并无影响。

js 复制代码
const WaterMarkCom = memo(({Component, props}) => {
    return (
      <View
        style={{
          height: '100%',
          width: '100%',
          position: 'relative',
        }}>
        <View
          style={{
            position: 'absolute',
            top: 0,
            right: 0,
            zIndex: -99,
            bottom: 0,
            left: 0,
          }}>
          <Component {...props} />
        </View>

        <WaterMark />
      </View>
    );
  }, []);


 const WaterComponent = props => {
    return <WaterMarkCom props={props} Component={item.component} />;
  };

至于我为什么套了一层,是因为莫名出现了有的视图被遮掩的情况。

waterMark组件的实现

其实我觉得最难的就是如何把数据内嵌进去,由于技术运气等其他原因,我没有使用webView中essage的方法将数据传递给html。去渲染页面,而是使用的injectedJavascript和使用reload更新这种办法来实现的动态添加水印。

下面为代码,免责声明,关于html生成水印的办法,我是照搬掘金内一个用户的代码

js 复制代码
import React, {memo, useRef} from 'react';
import moment from 'moment';
import {useEffect} from 'react';
import {useSelector} from 'react-redux';
import {WebView} from 'react-native-webview';
import {View, Platform, Dimensions} from 'react-native';

const WaterMark = () => {
  const markRef = useRef();
  const user = useSelector(state => state?.user?.value);

  const renderTextJs = function (user) {
  // 我使用的是这里添加来防止页面不渲染
    let arr = [];
    if (user.name) {
      arr = [`${user.name}`, `${user.tel}`, moment().format('YYYY/MM/DD')];
    } else {
      arr = ['', '', '']; // 退出登录后,置为空字符串
    }

    return `
		// 参数
		// str---水印文本  用来展示的文字
		// id---唯一ID     当前水印区域的唯一标识,用于区分多个区域水印
		// dom---DOM元素类名 传入类名则为区域水印,否则全屏水印
		const setWatermark = (strArr, id, dom) => {
			// 如果页面上已经存在该ID的水印元素,则先移除
			if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id));
			// 创建一个canvas元素,用于绘制水印
			const can = document.createElement('canvas');
			// 设置canvas的宽度和高度
			can.width = 180;
			can.height = 120;
			// 获取canvas的2d绘图上下文
			const cans = can.getContext('2d');
			// 旋转绘图上下文,设置水印的倾斜角度
			cans.rotate((-20 * Math.PI) / 180);
			// 设置水印文本的字体样式和大小
			cans.font = '14px KaiTi';
			// 设置水印文本的颜色和透明度
			cans.fillStyle = 'rgba(200, 200, 200, 0.50)';
			// 设置水印文本的基线对齐方式
			cans.textBaseline = 'middle';

			// 在canvas上绘制水印文本
			cans.fillText(strArr[0], 20, 60);
			cans.fillText(strArr[1], 20, 80);
			cans.fillText(strArr[2], 20, 100);


			// 创建一个变量用于接收DOM
			let div;
			// 如果传入了dom参数,则获取该类名的DOM元素,将水印添加到该元素上
			if (dom) {
				div = document.querySelector('.' + dom);
			}
			// 设置div元素的ID,用于后续识别和移除水印
			div.id = id;
			// 设置div元素不参与鼠标事件,避免影响页面其他元素的交互
			div.style.pointerEvents = 'none';
			// 设置div元素的z-index层级非常高,确保其显示在页面其他元素之上
			div.style.zIndex = '10000000';

			// 设置div元素的背景为水印图片,并设置为重复显示,从而铺满整个页面
			div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat';
			// 返回水印元素的ID,可用于后续移除水印
			return id;
		  };
			setWatermark(${JSON.stringify(arr)}, 11, 'box');
	`;
  };

  useEffect(() => {
    if (user.name) {
      markRef.current.reload();
    }
  }, [user]);

  return (
    <View
      pointerEvents="none"
      style={{
        height: Dimensions.get('window').height,
        width: Dimensions.get('window').width + 20,
        position: 'absolute',
        left: -20, //左侧空白太多
        bottom: 0,
        top: 0,
        right: 0,
        backgroundColor: 'transparent', // 透明
      }}>
      <WebView
        ref={markRef}
        injectedJavaScript={renderTextJs(user)}
        style={{
          height: Dimensions.get('window').height,
          width: Dimensions.get('window').width + 20, //添加上左移位置,防止右侧不渲染
          backgroundColor: 'transparent',
        }}
        scalesPageToFit={Platform.OS !== 'ios'}
        originWhitelist={['*']}
        useWebKit={true} // ios使用最新webkit内核渲染
        allowUniversalAccessFromFileURLs={true}
        geolocationEnabled={true}
        mixedContentMode={'always'}
        scrollEnabled={false}
        javaScriptEnabled={true}
        onMessage={event => {}}
        source={{
          html: `<!DOCTYPE html>
		<html lang="en">
		<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Watermark</title>
		</head>
		<body>
		<div class="box"></div>
		</body>
                <style>
		.box {
		height: 1000px;
		width: 1000px;
		overflow: hidden;
                background-color:transparent !important;
		}
                </style>
            </html>`,
        }}
      />
    </View>
  );
};

export default memo(WaterMark);

上述就是代码实现了,目前我是能实现app全局加水印的,对于未登录等情况也进行了一个兼容性处理

我分享出来,是因为,我在网上搜,react-native加水印,里面的内容都比较老,或者不太易读,我分享出来也算是做一个思路扩展。

有更好的方式,希望有底下留言,这样以后大家遇到问题,也能更好的解决问题。

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui