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加水印,里面的内容都比较老,或者不太易读,我分享出来也算是做一个思路扩展。

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

相关推荐
y先森2 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy2 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189112 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿3 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡4 小时前
commitlint校验git提交信息
前端
虾球xz4 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇5 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒5 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员5 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐5 小时前
前端图像处理(一)
前端