前端使用pdf.js渲染pdf文件解決方案
一、前言
在前端開發(fā)中,渲染PDF文件一直是一項(xiàng)重要而挑戰(zhàn)性的任務(wù)。而今,我們可以借助pdf.js庫來輕松實(shí)現(xiàn)這一目標(biāo)。pdf.js是一個(gè)開源的JavaScript庫,它可以在瀏覽器中渲染PDF文件,實(shí)現(xiàn)了在網(wǎng)頁上查看PDF文檔的功能。它提供了豐富的API和功能,使得在前端頁面展示PDF文件變得輕而易舉。讓我們一起探索pdf.js的奇妙之處,輕松實(shí)現(xiàn)前端PDF文件的渲染與展示吧!
二、簡介
1、pdf.js介紹
pdf.js是一款基于JavaScript的開源PDF閱讀器組件,可以在網(wǎng)頁中直接顯示和操作PDF文件,目前已知的前端渲染pdf組件都是基于pdf.js進(jìn)行封裝。
git地址:https://github.com/mozilla/pdf.js
注:開源且免費(fèi)
2、插件版本參數(shù)
| 插件 | 版本 |
| Node | v22.13.0 |
@types/react | ^18.0.33 |
@types/react-dom | ^18.0.11 |
| pdfjs-2.5.207-es5-dist.zip (viewer.js使用方式) | 2.5.207 |
| pdfjs-dist (canvas渲染方式) | 3.6.172 |
三、通過viewer.html實(shí)現(xiàn)預(yù)覽(推薦)
1、介紹
除了PDF預(yù)覽,還待配套的工具欄,支持功搜索、縮放、目錄、打印等功能~
Demo如圖:

2、部署
【1】下載插件包
下載地址:https://github.com/mozilla/pdf.js/releases/tag/v2.5.207

【2】客戶端方式
把下載后的pdfjs-2.5.207-es5-dist.zip解壓后,放在項(xiàng)目中的public文件夾下

【3】服務(wù)端方式
pdf.js包仍然放在public目錄下(或者服務(wù)端目錄下)
const pdfServerUrl = '/pdfjs-2.5.207-es5-dist/web/viewer.html'
一個(gè)可以獲取pdf文件二進(jìn)制流的地址(需要同源)
....
const pdfInfoUrl = `${location.origin}/xxx/xx.pdf`;
const url = `${pdfServerUrl}?file=${encodeURIComponent(pdfInfoUrl)}`
...
<iframe id='pdfIframe' src={url} width="100%" height="100%"></iframe>3、使用方法
【1】預(yù)覽PDF文件
1)客戶端方式(基于React框架為例)
const viewPDF: React.FC = () => {
// pdf文件路徑,放在項(xiàng)目的public目錄下
const pdfUrl = '/A.pdf';
//pdf.js庫的代碼,放在項(xiàng)目的public目錄下
const pdfServerUrl = '/pdfjs-2.5.207-es5-dist/web/viewer.html'
const url = `${pdfServerUrl}?file=${pdfUrl}`
return <>
<h1>pdf 搜索(基于pdf-dist,pdf_viewer.html)</h1>
<iframe id='pdfIframe' src={url} width="100%" height="100%"></iframe>
</>;
}2)服務(wù)端方式
通過axios接口獲取文件的arraybuffer,再把arraybuffer轉(zhuǎn)換成二進(jìn)制Blob,最后把Bolb轉(zhuǎn)成blob:url傳給viewer.html
...
import axios from 'axios';
...
const [pdfUrl, setPdfUrl] = useState<string>('');
const getPDFViewUrl = (fileName: any) => {
axios({
method: 'get',
url: `URL`,//文件下載的url
responseType: 'arraybuffer'
}).then(response =>{
const blob = new Blob([response.data], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
console.log('zyk===>', blobUrl)
setPdfUrl(`${pdfServerUrl}?file=${blobUrl}`)
})
};
return (
<>
<iframe id="pdfIframe" src={pdfUrl} width="100%" height="100%"></iframe>
</>
);【2】外部搜索條件觸發(fā)pdf.js的搜索邏輯
- 跳轉(zhuǎn)至第一個(gè)匹配的內(nèi)容
- 匹配內(nèi)容高亮
const viewPDF: React.FC = () => {
// pdf文件路徑,放在項(xiàng)目的public目錄下
const pdfUrl = '/A.pdf';
//pdf.js庫的代碼,放在項(xiàng)目的public目錄下
const pdfServerUrl = '/pdfjs-2.5.207-es5-dist/web/viewer.html'
const url = `${pdfServerUrl}?file=${pdfUrl}`
let pdfContentWindow: any = null; //緩存iframContent
const getPdfContent = () => {
const pdfFrame: any = document.getElementById('pdfIframe');
if (!pdfFrame) {
return;
}
pdfContentWindow = pdfFrame.contentWindow;
//pdf組件部分信息,包括:當(dāng)前頁碼、總共頁碼等
console.log('page===>', pdfContentWindow.PDFViewerApplication);
}
const onSearchForOut = (searchText: string) => {
pdfContentWindow.postMessage(searchText, '*');
pdfContentWindow.addEventListener('message', (e: any) => {
// 高亮匹配結(jié)果
pdfContentWindow.PDFViewerApplication.findBar.findField.value = e.data;
pdfContentWindow.PDFViewerApplication.findBar.highlightAll.checked = true; pdfContentWindow.PDFViewerApplication.findBar.dispatchEvent('highlightallchange');
//觸發(fā)搜索項(xiàng)‘下一個(gè)'事件
pdfContentWindow.PDFViewerApplication.findBar.dispatchEvent('again', false);
}, false);
}
useEffect(() => {
getPdfContent();
setTimeout(() => {
// 外部的搜索條件
onSearchForOut('陽區(qū)CBD核心區(qū)')
}, 3* 1000)
}, []);
return <>
<h1>pdf 搜索(基于pdf-dist,pdf_viewer.html)</h1>
<iframe id='pdfIframe' src={url} width="100%" height="100%"></iframe>
</>;
}四、把pdf渲染為canvas實(shí)現(xiàn)預(yù)覽
1、安裝
npm install pdfjs-dist --save
2、功能實(shí)現(xiàn)
【1】實(shí)現(xiàn)pdf預(yù)覽
import { Button } from 'antd';
import { useState, useEffect, useRef } from 'react';
import * as pdfjsLib from 'pdfjs-dist'; // 引入pdfjs-dist
const pdfUrl = '/zyk.pdf'; // pdf 文件路徑,pdf文件存放于public目錄下
const workerUrl = `/pdf.worker.min.js`; //webworker存放于public目錄下
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;
const viewPdf = (props: {height: string}) => {
const {height} = props;
const pdfContainerRef = useRef<any>(null);
const [pagesList, setPagesList] = useState<any>([]);
const scale = 2; // 縮放比例
// 渲染單個(gè)頁面
const renderPage = async (page: any, pageNumber: number) => {
const viewport = page.getViewport({ scale });
const pageContentDom = document.createElement('div');
pageContentDom.id = `pdfPage-content-${pageNumber}`;
pageContentDom.style.width = `${viewport.width}px`;
pageContentDom.style.height = `${viewport.height}px`;
pageContentDom.style.position = 'relative';
// 創(chuàng)建 Canvas 元素
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.id=`pdfPage-${pageNumber}`
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.style.border = '1px solid black';
pageContentDom.appendChild(canvas);
pdfContainerRef.current.appendChild(pageContentDom);
// 渲染 PDF 頁面到 Canvas
await page.render({
canvasContext: context,
viewport,
}).promise;
};
// 渲染 PDF 頁面
const renderPagesGroup = ( pages: any) => {
pages.forEach(({page}:any, index: number) => {
renderPage(page, index);
});
};
// 加載 PDF 文件
const loadPdf = async (url: any) => {
const pdf = await pdfjsLib.getDocument(url).promise;
const pages: any[] = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const textContent = await page.getTextContent();
pages.push({
page,
textContent
});
}
setPagesList(pages);
renderPagesGroup(pages);
};
useEffect(() => {
loadPdf(pdfUrl);
}, []);
return <>
<div>
<h1>PDF 搜索(基于@pdfjs-dist-自定義實(shí)現(xiàn))</h1>
<div>
<div style={{ height: height || '500px' }}>
{/* PDF 容器 */}
<div ref={pdfContainerRef} style={{ position: 'relative', height: '100%', overflowY: 'scroll' }} />
</div>
</div>
</div>
</>
};
export default viewPdf;
【2】實(shí)現(xiàn)pdf內(nèi)容文本可選進(jìn)行復(fù)制
...
//基于“【1】實(shí)現(xiàn)pdf預(yù)覽”代碼, 修改renderPage方法
// 渲染單個(gè)頁面
const renderPage = async (page: any, pageNumber: number) => {
const viewport = page.getViewport({ scale });
const pageContentDom = document.createElement('div');
pageContentDom.id = `pdfPage-content-${pageNumber}`;
//add-begin: 文本可選則 為了文本層和canvas層重疊,利用組件庫的類名(類名不能修改)
pageContentDom.className = 'pdfViewer';
pageContentDom.style.setProperty('--scale-factor', scale as any);
//add-end: 文本可選則
pageContentDom.style.width = `${viewport.width}px`;
pageContentDom.style.height = `${viewport.height}px`;
pageContentDom.style.position = 'relative';
// 創(chuàng)建 Canvas 元素
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.id=`pdfPage-${pageNumber}`
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.style.border = '1px solid black';
pageContentDom.appendChild(canvas);
createHeightLightCanvas(viewport, pageNumber, pageContentDom);
pdfContainerRef.current.appendChild(pageContentDom);
// 渲染 PDF 頁面到 Canvas
await page.render({
canvasContext: context,
viewport,
}).promise;
//add-begin: 文本可選則
const textLayerDiv = document.createElement('div');
textLayerDiv.style.width = viewport.width;
textLayerDiv.style.height = viewport.height;
//為了文本層和canvas層重疊,利用組件庫的類名
textLayerDiv.className = 'textLayer';
const textContent = await page.getTextContent();
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: viewport,
textDivs: [],
});
pageContentDom.appendChild(textLayerDiv);
//add-end: 文本可選則
};
【3】實(shí)現(xiàn)搜索,匹配內(nèi)容高亮,并且可以跳轉(zhuǎn)至匹配內(nèi)容的位置
import { Button } from 'antd';
import { useState, useEffect, useRef } from 'react';
import * as pdfjsLib from 'pdfjs-dist'; // 引入pdfjs-dist
const pdfUrl = '/zyk.pdf'; // pdf 文件路徑,pdf文件存放于public目錄下
const workerUrl = `/pdf.worker.min.js`; //webworker存放于public目錄下
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;
const viewPdf = (props: {height: string}) => {
const {height} = props;
const [searchText, setSearchText] = useState('');
const pdfContainerRef = useRef<any>(null);
const [pagesList, setPagesList] = useState<any>([]);
const [matchList, setMatchList] = useState<any>([]);
const scale = 2; // 縮放比例
const createHeightLightCanvas = (viewport: any, pageNumber: number, parentDom: any) => {
// 為每頁創(chuàng)建一個(gè)高亮層canvas
const highlightCanvas = document.createElement('canvas');
highlightCanvas.id = `highlightCanvas-${pageNumber}`;
highlightCanvas.className = 'highlightCanvas';
highlightCanvas.width = viewport.width;
highlightCanvas.height = viewport.height;
highlightCanvas.style.position = 'absolute';
highlightCanvas.style.top = '0';
highlightCanvas.style.left = '0';
highlightCanvas.style.zIndex = '1';
parentDom.appendChild(highlightCanvas);
}
// pageNumber 頁碼(從0開始)
const jumpToPage = (pageIndex: number) => {
let beforeCanvasHeight = 0;
for (let i = 0; i < pageIndex; i++) {
const canvasParentDom = pdfContainerRef.current.querySelector(`#pdfPage-content-${i}`);
let canvasParentHeight = canvasParentDom.style.height.replace('px', '');
beforeCanvasHeight += Number(canvasParentHeight);
}
pdfContainerRef.current.scrollTo({
top: beforeCanvasHeight, // 垂直滾動(dòng)位置
behavior: 'smooth'
});
}
const getCurrentTextContentY = (canvas: any, match: any) => {
// pdfjs 坐標(biāo)系原點(diǎn)在左下角。transform[5]代表y軸的基線,所以需要減去高度
const {textBlock} = match;
const { transform, height } = textBlock;
return canvas.height - (transform[5] + height -2) * scale;
}
// 滾動(dòng)到指定的匹配項(xiàng)
const scrollToMatch = (match: any) => {
const { pageIndex, matchList } = match;
const firstMatchContent = matchList[0];
// 獲取滾動(dòng)區(qū)域的高度
const scrollHeight = pdfContainerRef.current.scrollHeight;
console.log('滾動(dòng)區(qū)域的高度:', scrollHeight);
// 獲取當(dāng)前頁碼之前dom的高度
let beforePageHeight = 0;
for (let i = 0; i < pageIndex; i++) {
const canvasParentDom = pdfContainerRef.current.querySelector(`#pdfPage-content-${i}`);
let canvasParentHeight = canvasParentDom.style.height.replace('px', '');
beforePageHeight += Number(canvasParentHeight);
}
// todo 繼續(xù)計(jì)算 匹配項(xiàng)目的高度
const currentPageCanvas = pdfContainerRef.current.querySelector(`#pdfPage-${pageIndex}`);
const textContentY = getCurrentTextContentY(currentPageCanvas, firstMatchContent);
const offsetTop = 50; //為了滾動(dòng)目標(biāo)文字不頂格
const targetScrollTop = beforePageHeight + textContentY -offsetTop;
pdfContainerRef.current.scrollTo({
top: targetScrollTop, // 垂直滾動(dòng)位置
behavior: 'smooth'
});
};
// 繪制高亮區(qū)域
const drawHighlights = async (canvas: any, matchesList: MatchBlockItem[]) => {
if (matchesList.length === 0) {
return;
}
const context = canvas.getContext('2d');
context.fillStyle = 'rgba(255, 255, 0, 0.5)'; // 黃色半透明填充
matchesList.forEach((match: any) => {
const {textBlock} = match;
const { transform, width, height, str } = textBlock;
// 獲取每一個(gè)字符的寬度
const charWidth = width / str.length;
const lightWidth = (match.textEndIndex - match.textStartIndex) * charWidth;
const lightHeight = height;
const x = transform[4] + match.textStartIndex * charWidth;
const y = getCurrentTextContentY(canvas, match);
context.fillRect(
Math.floor(x * scale),
Math.floor(y),
Math.ceil(lightWidth * scale),
Math.ceil(lightHeight * scale)
);
});
};
// 渲染單個(gè)頁面
const renderPage = async (page: any, pageNumber: number) => {
const viewport = page.getViewport({ scale });
const pageContentDom = document.createElement('div');
pageContentDom.id = `pdfPage-content-${pageNumber}`;
//為了文本層和canvas層重疊,利用組件庫的類名
pageContentDom.className = 'pdfViewer';
pageContentDom.style.setProperty('--scale-factor', scale as any);
pageContentDom.style.width = `${viewport.width}px`;
pageContentDom.style.height = `${viewport.height}px`;
pageContentDom.style.position = 'relative';
// 創(chuàng)建 Canvas 元素
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.id=`pdfPage-${pageNumber}`
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.style.border = '1px solid black';
pageContentDom.appendChild(canvas);
createHeightLightCanvas(viewport, pageNumber, pageContentDom);
pdfContainerRef.current.appendChild(pageContentDom);
// 渲染 PDF 頁面到 Canvas
await page.render({
canvasContext: context,
viewport,
}).promise;
// 渲染文本框
const textLayerDiv = document.createElement('div');
textLayerDiv.style.width = viewport.width;
textLayerDiv.style.height = viewport.height;
//為了文本層和canvas層重疊,利用組件庫的類名
textLayerDiv.className = 'textLayer';
const textContent = await page.getTextContent();
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: viewport,
textDivs: [],
});
pageContentDom.appendChild(textLayerDiv)
};
// 渲染 PDF 頁面
const renderPagesGroup = ( pages: any) => {
pages.forEach(({page}:any, index: number) => {
renderPage(page, index);
});
};
// 加載 PDF 文件
const loadPdf = async (url: any) => {
const pdf = await pdfjsLib.getDocument(url).promise;
const pages: any[] = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const textContent = await page.getTextContent();
pages.push({
page,
textContent
});
}
setPagesList(pages);
renderPagesGroup(pages);
};
const findAllMatches = (text: string, pattern: string) => {
// 創(chuàng)建正則表達(dá)式對象
const regex = new RegExp(pattern, 'g');
// 使用match方法找到所有匹配項(xiàng)
const matches = text.match(regex);
// 如果沒有匹配項(xiàng),返回空數(shù)組
if (!matches) {
return [];
}
// 創(chuàng)建一個(gè)數(shù)組來存儲所有匹配的位置
const positions = [];
// 遍歷所有匹配項(xiàng),找到它們在字符串中的位置
let match;
while ((match = regex.exec(text)) !== null) {
positions.push(match.index);
}
return positions;
}
// todo 優(yōu)化參數(shù)個(gè)數(shù),
const getMatchesList = (
items: any,
currentItem: any,
currentItemIndex: number,
currentTextIndex: number,
searchStr: string): MatchBlockItem[] => {
let matchSearchList: MatchBlockItem[] = [];
if(currentItem.str.length - (currentTextIndex + 1) < searchStr.length -1 ) {
// 獲取當(dāng)前文本塊中剩余字符,如果小于搜索字符長度,則繼續(xù)查找下一個(gè)文本塊
let itemText = currentItem.str.slice(currentTextIndex); // 獲取當(dāng)前文本塊中剩余字符
let tempMatchSearchList = [{
blockIndex: currentItemIndex,
textStartIndex: currentTextIndex,
textEndIndex: currentItem.str.length,// 由于統(tǒng)一使用slice截取,所以不包括最后一位
textBlock: currentItem
}]; // 存儲后續(xù)文本塊
let index = currentItemIndex;
const otherSearchLength = searchStr.length -1;
while (itemText.length <= otherSearchLength) {
index = index + 1;
const currentOtherSearchLength = otherSearchLength - itemText.length; // 當(dāng)前剩余搜索字符長度
if (items[index].str.length > currentOtherSearchLength) {
// 文本塊的長度大于剩余搜索字符長度,則截取剩余搜索字符長度的字符
itemText = `${itemText}${items[index].str.slice(0, currentOtherSearchLength+1)}`;
tempMatchSearchList.push({
blockIndex: index,
textStartIndex: 0,
textEndIndex: currentOtherSearchLength + 1,
textBlock: items[index]
})
} else {
// 文本塊的長度小于剩余搜索字符長度,則截取全部字符, 繼續(xù)
itemText = `${itemText}${items[index].str}`;
tempMatchSearchList.push({
blockIndex: index,
textStartIndex: 0,
textEndIndex: items[index].str.length,
textBlock: items[index]
})
}
}
if (itemText === searchStr) {
matchSearchList = matchSearchList.concat(tempMatchSearchList);
}
}
else {
// 獲取當(dāng)前文本塊中剩余字符,如果大于等于搜索字符長度,則截取當(dāng)前文本塊中搜索文本長度的字符
const textEndIndex = currentTextIndex + searchStr.length;
const text = currentItem.str.slice(currentTextIndex, textEndIndex); // 取出匹配字符所在文本塊及后續(xù)文本塊
if (text === searchStr) {
console.log('匹配到了:', currentItem, currentItemIndex)
matchSearchList.push({
blockIndex: currentItemIndex,
textStartIndex: currentTextIndex,
textEndIndex: textEndIndex,
textBlock: currentItem
})
}
}
return matchSearchList;
}
// 查找文本的所有出現(xiàn)位置
const findAllOccurrences = (items: any, searchStr: string): MatchBlockItem[] => {
const firstSearchStr = searchStr[0];
let matchSearchList: MatchBlockItem[] = [];
for(let i=0; i<items.length; i++) {
const currentItem = items[i];
const currentMatchIndexList = findAllMatches(currentItem.str, firstSearchStr); // 獲取當(dāng)前文本塊中第一個(gè)匹配字符的索引列表
if (currentMatchIndexList.length > 0) {
for(let j=0; j<currentMatchIndexList.length; j++){
matchSearchList = [...matchSearchList, ...getMatchesList(items, currentItem, i, currentMatchIndexList[j], searchStr)];
}
}
}
return matchSearchList;
};
const clearHeightLightsCanvas = () => {
const highlightCanvases = Array.from(pdfContainerRef.current.querySelectorAll('.highlightCanvas'));
highlightCanvases.forEach((canvas: any) => {
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
});
}
const handleSearch = async () => {
clearHeightLightsCanvas()
if (!searchText) {
jumpToPage(0);
return;
}
const newMatches: any = [];
console.log('pagesList', pagesList)
// todo 目前是按照每頁來匹配,可能會匹配不到跨頁的內(nèi)容
pagesList.forEach(async ({textContent}: any, pageIndex: number) => {
const pageMatches = findAllOccurrences(textContent.items, searchText);
newMatches.push({
pageIndex, // 頁面索引
matchList: pageMatches, // 匹配項(xiàng)列表
});
})
console.log('newMatches', newMatches);
const isNotMatch = newMatches.every((match: any) => match.matchList.length === 0);
if (isNotMatch) {
alert('未找到匹配項(xiàng)');
return;
}
/// 重新繪制高亮區(qū)域
pagesList.forEach((_: any, pageIndex: number) => {
const highlightCanvas = pdfContainerRef.current.querySelectorAll('.highlightCanvas')[pageIndex]; // 獲取高亮層 Canvas
const currentMatches = newMatches.find((match: any) => match.pageIndex === pageIndex);
drawHighlights(
highlightCanvas,
currentMatches?.matchList || []
);
});
// 跳轉(zhuǎn)
const isExistItem = newMatches.find((match: any) => match.matchList.length > 0);
if (isExistItem) {
scrollToMatch(isExistItem);
}
};
// 初始化 PDF.js
useEffect(() => {
loadPdf(pdfUrl);
}, []);
return <>
<div>
<h1>PDF 搜索(基于@pdfjs-dist-自定義實(shí)現(xiàn))</h1>
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="輸入要搜索的內(nèi)容"
/>
<Button onClick={handleSearch}>搜索</Button>
<div>
<div style={{ height: height || '500px' }}>
{/* PDF 容器 */}
<div ref={pdfContainerRef} style={{ position: 'relative', height: '100%', overflowY: 'scroll' }} />
</div>
</div>
</div>
</>
};
export default viewPdf;
【4】獲取pdf文件中目錄的數(shù)據(jù)結(jié)構(gòu)
....
//基于‘【1】實(shí)現(xiàn)pdf預(yù)覽'的代碼
const get= async (url: any) => {
const pdf = await pdfjsLib.getDocument(url).promise;
// 獲取目錄數(shù)據(jù)
const pdfCatalogue= await pdf.getOutline();
console.log('目錄數(shù)據(jù):', pdfCatalogue);
};
...
總結(jié)
到此這篇關(guān)于前端使用pdf.js渲染pdf文件解決方案的文章就介紹到這了,更多相關(guān)前端pdf.js渲染pdf文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript的級聯(lián)函數(shù)用法簡單示例【鏈?zhǔn)秸{(diào)用】
這篇文章主要介紹了JavaScript的級聯(lián)函數(shù)用法,結(jié)合簡單實(shí)例形式分析了javascript鏈?zhǔn)秸{(diào)用具體定義及使用方法,需要的朋友可以參考下2019-03-03
JavaScript 中的12種循環(huán)遍歷方法【總結(jié)】
本文給大家總結(jié)了12種JavaScript 中的1循環(huán)遍歷方法,每種方法通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05
JS動(dòng)態(tài)創(chuàng)建Table,Tr,Td并賦值的具體實(shí)現(xiàn)
這篇文章介紹了JS動(dòng)態(tài)創(chuàng)建Table,Tr,Td并賦值的具體實(shí)現(xiàn),有需要的朋友可以參考一下2013-07-07
Three.js實(shí)現(xiàn)臉書元宇宙3D動(dòng)態(tài)Logo效果
本文主要講述通過 Three.js + Blender 技術(shù)棧,實(shí)現(xiàn) Meta 公司炫酷的 3D 動(dòng)態(tài) Logo,內(nèi)容包括基礎(chǔ)模型圓環(huán)、環(huán)面扭結(jié)、管道及模型生成、模型加載、添加動(dòng)畫、添加點(diǎn)擊事件、更換材質(zhì)等2021-11-11
淺析JavaScript中的特殊函數(shù)及用法小結(jié)
JavaScript中的函數(shù)本質(zhì)上是一個(gè)對象,我們可以將這個(gè)對象賦值給一個(gè)變量,這就使JavaScript中的函數(shù)變得非常的靈活,現(xiàn)在就來淺看一下JavaScript中函數(shù)的一些用法,需要的朋友可以參考下2022-06-06
原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果
本文主要分享了原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果的示例代碼,并解析了實(shí)例中的注意點(diǎn)。具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01
bootstrap基礎(chǔ)知識學(xué)習(xí)筆記
這篇文章主要針對bootstrap基礎(chǔ)知識為大家整理了詳細(xì)的學(xué)習(xí)筆記,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
JavaScript Array.flat()函數(shù)用法解析
這篇文章主要介紹了JavaScript Array.flat()函數(shù)用法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
javascript實(shí)現(xiàn)簡單的頁面右下角提示信息框
本文給大家分享的是使用javascript實(shí)現(xiàn)簡單的頁面右下角提示信息框的方法和示例代碼,有需要的小伙伴可以參考下。2015-07-07

