iframe渲染pdf

3,077 阅读5分钟

iframe渲染pdf实验

源码:github.com/skylar2826/…

实验一:iframe引入前端静态资源

<iframe src="./1.pdf"></iframe>

无脚手架,直接在html文件中引入iframe

使用方式结果结果演示
image-20230331161223914.png正常展示pdfimage-20230331161242668.png

使用脚手架如vite, 在vue文件中引入iframe

使用方式结果结果演示
image-20230331161404166.png不能展示pdfimage-20230331161504362.png
问题研究
src="./1.pdf"的相对路径找不到文件资源

当你yarn serve启动项目后,src相对路径是相对yarn serve执行的那层目录而非源码所在目录路径;

序号解决方案配图
方案一(bad)将pdf资源移动到根路径下image-20230331165612475.png
方案二(bad)将路径修改为‘./src/1.pdf’image-20230331165931622.png
方案三(规范)将静态资源放在根目录下的assets文件夹下,使用相对于服务器根路径的路径image-20230331170114307.png
方案四(可靠)使用import.meta.url, 表明以当前文件目录所在位置为相对开始的位置创建一个绝对路径的URLimage-20230331170338613.png
找不到文件,为什么递归展示了三层iframe

不太清楚。下面是ChatGPT给我发HTML5规范,大体意思就是iframe设计就是src中指定资源找不到就尝试重新加载iframe所在的同一页面。

If the src attribute on the iframe element is set to the same URL as the document containing the iframe element, the user agent may instead navigate the nested browsing context to the top-level browsing context's active document.

小结
  • 静态资源没有被打包编译,也没有被存储在特定的位置。只作为普通文件直接从源码中读取使用。// 除非你额外配置
  • import.meta.url 指明当前文件所在目录位置,import.meta是这个模块的元信息

实验二:后端返回pdf文件流,前端iframe渲染

核心源码

前端
<template>
  <iframe :src="url"></iframe>
</template>
<script>
import {ref} from 'vue'
export default {
  setup() {
    let url = ref('');
    const getPdf = () => {
      const xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          const blob = new Blob([xhr.response], {type: 'application/pdf'});
          url.value = URL.createObjectURL(blob);
        }
      }
      xhr.responseType = "blob"; // 不设置,无法正确解析pdf文件
      xhr.open("GET", "http://localhost:8000/getPdf1", true);
      xhr.send();
    }
​
    getPdf();
    return {url}
  }
}
</script>
后端
const express = require('express');
const app = express();
​
app.get('/getPdf1', (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.sendFile(`${__dirname}/1.pdf`);
})
app.listen(8000, () => {
  console.log("服务已启动,8000端口监听中...")
})

其他:隐藏toolbar

toolbar展示场景: 当iframe pdf宽高为100%时,会展示toolbar。

隐藏前演示隐藏方式隐藏后演示
image-20230403150922165-16805058439617.png<iframe :src="url + '#toolbar=0'" ></iframe>image-20230403150948879.png

iframe相关简介

HTML内联框架元素(<iframe>)表示嵌套的browsing context,它能将另一个html页面嵌入当前页面。

browsing context 浏览器上下文

  • browsing context是浏览器展示Document的环境。在现代浏览器中,通常是一个标签页(tab),也可能是一个窗体(window)或者页面的一部分(iframe);
  • 每个browsing context都有一个活动文档的源(origin)和一个记录所有展示文档的有序历史(history);
  • browsing context之间的通讯被严格限制,只有两个同源的浏览器上下文才能打开和使用通讯接口(BroadcastChannel);

BroadcastChannel通讯接口

该接口可以实现同源的不同窗体、tab页、iframe下的不同Document之间相互通讯。通过触发message事件,消息可以广播到所有监听了该事件的BroadcastChannel对象。

// 事件发送者
const channel = new BroadcastChannel('expample1');
function handleClick() {
    channel.postMessage('触发事件');
}
// 事件接收者
const channel = new BroadcastChannel('expample1');
channel.addEventListener('message', (event) => {
    console.log("接收内容", event.data);
})

iframe废弃属性

  • frameborder:设置边框,使用css border代替
  • scrolling: 是否在框架内显示滚动条

iframe中样式修改实验

iframe是可替换元素,可替换元素的展示效果不由css控制。就是说它们的内容不受当前文档样式的影响,css可以影响可替换元素的位置(object-fit、object-position),但不会影响可替换元素自身的内容。iframe元素可以有自己的样式表,它们不会继承父文档的样式。

实验目标

修改滚动条样式,保证全局均使用期望样式滚动条

默认样式期望样式
image-20230403144443530.pngimage-20230403144422420.png

实验一:修改全局滚动条

核心源码演示
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb { background-color: #bfbfbf; -webkit-border-radius: 6px; }
image-20230403105009054.png

实验二:修改iframe中html的滚动条

修改前演示核心源码修改后演示
image-20230403105905093.pngconst setIframeStyle = () => {
  const iframe = instance.refs.iframeHtml.contentDocument;
  const style = document.createElement('style');
  iframe.head.appendChild(style);
  const sheet = style.sheet;
  sheet.insertRule("::-webkit-scrollbar {width: 6px;height: 6px;}");
  sheet.insertRule("::-webkit-scrollbar-thumb {background-color: #bfbfbf;-webkit-border-radius: 6px;}"); }
image-20230403144415305.png
问题研究:onMounted及之前触发setIframeStyle方法修改滚动条不生效

iframe加载异步,当父组件onMounted后,iframe还没有加载完成。所以在onMounted中拿不到加载完成的iframe,触发setIframeStyle也不生效。

解决方案:iframe提供onload方法,可以在onload方法中setIframeStyle。

实验代码演示
onMounted(() => {
  // 设置无效,iframe加载完成晚于onMounted
  // setIframeStyle();
  console.log("onMounted");
});
const handleLoad = () => {
  console.log("iframe加载完成");
  setIframeStyle(); }
image-20230403144541686.png

实验三:修改iframe中pdf的滚动条

核心代码演示
<pdf :src="url" />
import pdf from 'vue-pdf-embed'
export default {
  components: {pdf}
}
image-20230403170043945.png
问题研究一:如实验二方式修改iframe中全局滚动条样式不生效原因

pdf中滚动条是自带的,不支持修改。

解决方案:考虑使用第三方库,将iframe pdf转换成canvas后,即可使用实验一提供的能力。相较于iframe引入pdf, 第三方库将pdf转换成canvas具有更好的跨浏览器的兼容性,也保证样式统一。

相关资料

  1. <iframe>
  2. iframe 高度100%时,出现垂直滚动条
  3. 修改iframe内部元素的样式
  4. 修改滚动条样式
  5. 幽灵空白节点
  6. JS 动态改变伪元素的样式
  7. 这将是你看到过最全的pdf预览解决方案