菠萝荔枝饼 發表於 2024-7-15 23:22:00

JavaScript系列:JS实现复制粘贴文字以及图片

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>一. 基于 Clipboard API 复制文字(推荐)<ul><li>基本概念</li><li>主要方法</li><li>使用限制</li><li>实际应用示例</li></ul></li><li>二、基于 document.execCommand('copy')<ul><li>缺陷</li><li>实际应用示例</li><li>说明</li></ul></li><li>三、复制图片功能</li><li>四、封装</li></ul></div><p></p>
<h2 id="一-基于-clipboard-api-复制文字推荐">一. 基于 Clipboard API 复制文字(推荐)</h2>
<h3 id="基本概念">基本概念</h3>
<p>Clipboard API 是一组用于在浏览器中操作剪贴板的 JavaScript API,它允许开发者在网页上读取和写入剪贴板内容,实现复制、剪切和粘贴等功能。Clipboard API 提供了一种在网页上读取和写入剪贴板内容的方式,包括文本、图像和其他类型的数据。Clipboard API 适用于需要与用户剪贴板进行交互的网页应用,如实现一键复制、粘贴功能,或者在用户复制特定内容时自动添加额外信息等。</p>
<p>https://developer.mozilla.org/zh-CN/docs/Web/API/Clipboard_API</p>
<h3 id="主要方法">主要方法</h3>
<p>Clipboard API 提供了几个关键的方法来实现剪贴板的读写操作:</p>
<ol>
<li><strong>navigator.clipboard.writeText(text)</strong>:将给定的文本复制到剪贴板。这是一个异步方法,会返回一个 Promise 对象,成功时 Promise 会被解析,失败时会被拒绝。</li>
<li><strong>navigator.clipboard.readText()</strong>:从剪贴板读取文本内容。这也是一个异步方法,返回一个 Promise 对象,解析后提供剪贴板中的文本内容。</li>
<li><strong>navigator.clipboard.write(data)</strong>:写入更复杂的数据类型到剪贴板,如文件、图像等。data 参数是一个包含 ClipboardItem 对象的数组,每个 ClipboardItem 对象代表剪贴板中的一项数据。这也是一个异步方法,返回一个 Promise 对象。</li>
<li><strong>navigator.clipboard.read()</strong>:从剪贴板读取更复杂的数据类型,如文件、图像等。这个方法会返回一个 Promise 对象,解析后提供一个包含 ClipboardItem 对象的数组。</li>
</ol>
<h3 id="使用限制">使用限制</h3>
<ul>
<li><strong>用户授权</strong>:由于安全和隐私的考虑,浏览器在使用 Clipboard API 时通常需要用户授权。例如,在尝试从剪贴板读取或写入数据时,浏览器可能会要求用户明确允许。</li>
<li><strong>安全上下文</strong>:Clipboard API 只能在安全的环境中操作剪贴板,如 HTTPS 页面、localhost本机下。</li>
<li><strong>浏览器兼容性</strong>:虽然大多数现代浏览器都支持 Clipboard API,但仍有部分旧版浏览器可能不支持。因此,在使用时需要考虑浏览器的兼容性。</li>
</ul>
<h3 id="实际应用示例">实际应用示例</h3>
<pre><code class="language-js">&lt;template&gt;
    &lt;el-button type="primary" @click="handleCopy"&gt;复制文本&lt;/el-button&gt;
    &lt;div&gt;{{ message }}&lt;/div&gt;
&lt;/template&gt;
&lt;script setup&gt;
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const message = ref('复制的内容')

const handleCopy = () =&gt; {
    navigator.clipboard
      .writeText(message.value)
      .then(() =&gt; {
            ElMessage({
                message: '复制成功',
                type: 'success',
            })
      })
      .catch((err) =&gt; {
            console.error('复制失败:', err)
            ElMessage({
                message: '复制失败',
                type: 'error',
            })
      })
}
&lt;/script&gt;
</code></pre>
<h2 id="二基于-documentexeccommandcopy">二、基于 document.execCommand('copy')</h2>
<p><code>document.execCommand('copy')</code> 是一个在网页上执行复制操作的旧式API,属于 Web API 的一部分,用于在不需要用户交互(如点击或按键)的情况下,通过脚本复制文本到剪贴板。然而,这个API在现代Web开发中已经被视为过时(deprecated),并在许多现代浏览器中受到限制或不再支持,尤其是在没有用户明确交互的情况下。</p>
<p>https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand</p>
<h3 id="缺陷">缺陷</h3>
<ul>
<li>只能操作input, textarea或具有contenteditable属性的元素</li>
<li><code>execCommand</code> 是同步操作,如果复制/粘贴大量数据,可能会导致页面出现卡顿现象,影响用户体验。</li>
<li>它只能将选中的内容复制到剪贴板,无法向剪贴板任意写入内容</li>
<li>有些浏览器还会<strong>跳出提示框,要求用户许可</strong>,这时在用户做出选择前,页面会失去响应。</li>
</ul>
<h3 id="实际应用示例-1">实际应用示例</h3>
<pre><code class="language-js">&lt;template&gt;
    &lt;el-button type="primary" @click="handleCopy2"&gt;复制文本2&lt;/el-button&gt;
    &lt;div&gt;{{ message }}&lt;/div&gt;
&lt;/template&gt;
&lt;script setup&gt;
import {
    copyText,
    copyImage,
    imageUrlToBase64,
    parseBase64,
} from './common/copy'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const message = ref('复制的内容')

const handleCopy2 = () =&gt; {
    // 动态创建 textarea 标签
    const textarea = document.createElement('textarea')
    // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
    textarea.readOnly = 'readonly'
    textarea.style.position = 'absolute'
    textarea.style.left = '-9999px'
    textarea.style.opacity = '0'
    // 将要 copy 的值赋给 textarea 标签的 value 属性
    textarea.value = message.value
    // 将 textarea 插入到 body 中
    document.body.appendChild(textarea)
    // 选中值并复制
    textarea.select()
    const result = document.execCommand('Copy')
    if (result) {
      ElMessage({
            message: '复制成功',
            type: 'success',
      })
    }
    document.body.removeChild(textarea)
}
&lt;/script&gt;
</code></pre>
<h3 id="说明">说明</h3>
<p><code>clipboard.js</code> 底层也是基于 <code>document.execCommand</code>去实现的</p>
<pre><code class="language-js">function createFakeElement(value) {
var isRTL = document.documentElement.getAttribute('dir') === 'rtl';
var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS

fakeElement.style.fontSize = '12pt'; // Reset box model

fakeElement.style.border = '0';
fakeElement.style.padding = '0';
fakeElement.style.margin = '0'; // Move element out of screen horizontally

fakeElement.style.position = 'absolute';
fakeElement.style = '-9999px'; // Move element to the same position vertically

var yPosition = window.pageYOffset || document.documentElement.scrollTop;
fakeElement.style.top = "".concat(yPosition, "px");
fakeElement.setAttribute('readonly', '');
fakeElement.value = value;
return fakeElement;
}
var fakeCopyAction = function fakeCopyAction(value, options) {
var fakeElement = createFakeElement(value);
options.container.appendChild(fakeElement);
var selectedText = select_default()(fakeElement);
command('copy');
fakeElement.remove();
return selectedText;
};
</code></pre>
<h2 id="三复制图片功能">三、复制图片功能</h2>
<pre><code class="language-js">&lt;template&gt;
    &lt;el-button type="primary" @click="handleCopyImage"&gt;复制图片&lt;/el-button&gt;
    &lt;div&gt;{{ message }}&lt;/div&gt;
&lt;/template&gt;
&lt;script setup&gt;
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const message = ref('复制的内容')

const handleCopyImage = async () =&gt; {
    //具体看下面的封装
    await copyImage('https://cn.vitejs.dev/logo-with-shadow.png')
    ElMessage({
      message: '复制成功',
      type: 'success',
    })
}
&lt;/script&gt;
</code></pre>
<h2 id="四封装">四、封装</h2>
<pre><code class="language-js">/**
* 图片转base64
* @param {string} 图片地址
* @returns
*/
export const imageUrlToBase64 = (imageUrl) =&gt; {
    return new Promise((resolve, reject) =&gt; {
      let image = new Image()
      image.setAttribute('crossOrigin', 'Anonymous')
      image.src = imageUrl
      image.onload = function () {
            const canvas = document.createElement('canvas')
            canvas.width = image.width
            canvas.height = image.height
            const context = canvas.getContext('2d')
            context.drawImage(image, 0, 0, image.width, image.height)
            const base64Str = canvas.toDataURL('image/png')
            resolve(base64Str)
      }
      image.onerror = function (e) {
            reject(e)
      }
    })
}

/**
* 转换base64
* @param {string} base64
* @returns
*/
export function parseBase64(base64) {
    let re = new RegExp('data:(?&lt;type&gt;.*?);base64,(?&lt;data&gt;.*)')
    let res = re.exec(base64)
    if (res) {
      return {
            type: res.groups.type,
            ext: res.groups.type.split('/').slice(-1),
            data: res.groups.data,
      }
    }
}

/**
* 复制文字
* @param {string} text要复制的文本
* @returns {boolean} true/false
*/
export const copyText = async (text) =&gt; {
    if (navigator &amp;&amp; navigator.clipboard) {
      await navigator.clipboard.writeText(text)
      return true
    }
    // 动态创建 textarea 标签
    const textarea = document.createElement('textarea')
    // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
    textarea.readOnly = 'readonly'
    textarea.style.position = 'absolute'
    textarea.style.left = '-9999px'
    textarea.style.opacity = '0'
    // 将要 copy 的值赋给 textarea 标签的 value 属性
    textarea.value = text
    // 将 textarea 插入到 body 中
    document.body.appendChild(textarea)
    // 选中值并复制
    textarea.select()
    const result = document.execCommand('Copy')
    document.body.removeChild(textarea)
    return result
}

/**
* 复制图片
* @param {string} imageUrl 图片地址
* @param {boolean} isBase64 是否是base64
*/
export const copyImage = async (imageUrl, isBase64 = false) =&gt; {
    let base64Url = ''
    if (!isBase64) {
      base64Url = await imageUrlToBase64(imageUrl)
    } else base64Url = imageUrl
    const parsedBase64 = parseBase64(base64Url)
    let type = parsedBase64.type
    //将base64转为Blob类型
    let bytes = atob(parsedBase64.data)
    let ab = new ArrayBuffer(bytes.length)
    let ua = new Uint8Array(ab)
    for (let i = 0; i &lt; bytes.length; i++) {
      ua = bytes.charCodeAt(i)
    }
    let blob = new Blob(, { type })
    navigator.clipboard.write(: blob })])
}
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <p>微信:17873041739</p>
<p><img src="https://img2020.cnblogs.com/blog/1699002/202201/1699002-20220105164456073-1924927094.jpg" width='200px' height='200px' /></p><br><br>
来源:https://www.cnblogs.com/vic-tory/p/18304227
頁: [1]
查看完整版本: JavaScript系列:JS实现复制粘贴文字以及图片