Tostr
1、Tostr
Section titled “1、Tostr”toastr.options = { // toastr配置 "closeButton": true, //给消息框加一个可以主动关闭的 X // 自定义关闭的HTML 与上一个设置连用 可以出现效果 "closeHtml": '<button><i class="icon-off">自定义内容</i></button>' "debug": false, //是否使用debug模式 "progressBar": true, //是否显示进度条,当为false时候不显示;当为true时候,显示进度条,当进度条缩短到0时候,消息通知弹窗消失 "positionClass": "toast-top-center",//位置 "showDuration": "400", //显示的动画时间 "hideDuration": "1000", //消失的动画时间 "timeOut": "7000", //展现时间 "extendedTimeOut": "1000", // 当用户鼠标从消息上移开之后多久之后消失 "showEasing": "swing", //显示时的动画缓冲方式 "hideEasing": "linear", //消失时的动画缓冲方式 "showMethod": "fadeIn", //显示时的动画方式 "hideMethod": "fadeOut", //消失时的动画方式 "rtl": true, // 让文字从右往左排列 "preventDuplicates": true,// 防止重复 如果消息内容重复 只会显示第一条}// 如果想要让html标签正常在消息提示中显示,可以设置这个toastr.options.escapeHtml = true;
// 新的消息出现的顺序 默认新消息出现在上面 设置之后新消息出现在下面toastr.options.newestOnTop = false;
//为了防止消息自动消失 可以将以上两个时间设置为 0 点击后自动消失toastr.options.timeOut = 0;toastr.options.extendedTimeOut = 0;
// 点击关闭按钮时的动画设置toastr.options.closeMethod = 'fadeOut';toastr.options.closeDuration = 300;toastr.options.closeEasing = 'swing';
// 渐变效果toastr.options.showEasing = 'swing';toastr.options.hideEasing = 'linear';toastr.options.closeEasing = 'linear';
// 动画方法toastr.options.showMethod = 'slideDown';toastr.options.hideMethod = 'slideUp';toastr.options.closeMethod = 'slideUp';
//事件回调toastr.options.onShown = function() { console.log('hello'); } // 消息展示的回调toastr.options.onHidden = function() { console.log('goodbye'); }// 消息隐藏的回调toastr.options.onclick = function() { console.log('clicked'); } // 点击消息的回调toastr.options.onCloseClick = function() { console.log('close button clicked'); }//点击关闭按钮的回调
toastr.success('保存成功!');//移除所有,没有动画效果toastr.remove()//立即移除消息提示,带动画效果toastr.clear()2、Spin
Section titled “2、Spin”js 加载框
1、基本使用
Section titled “1、基本使用”var opts = { lines: 13, // line 的个数 length: 38, // line 的长度 width: 17, // 单个 line 的宽度 radius: 45, // loading 中心空白圆的大小 scale: 1, // 放大缩小 animation: 'spinner-line-shrink', // 动画样式 color: '#ffffff', // 言责 top: '50%', // Top position relative to parent left: '50%', // Left position relative to parent zIndex: 2000000000, // The z-index (defaults to 2e9) position: 'absolute', // Element positioning};//jsvar spinner = new Spin.Spinner(opts).spin(target); //show spinner.stop() //hide
//esmvar spinner = new Spinner().spin(); spinner.stop() //hide
//复用spinner对象targetEl.appendChild(spinner.el);2、spin 添加蒙板
Section titled “2、spin 添加蒙板”给
spin添加spin-mask,在spin.js中找到这段代码,并且替换
var Spinner = /** @class */ (function () { function Spinner(opts) { if (opts === void 0) { opts = {}; } this.opts = __assign(__assign({}, defaults), opts); } /** * Adds the spinner to the given target element. If this instance is already * spinning, it is automatically removed from its previous target by calling * stop() internally. */ Spinner.prototype.spin = function (target) { this.stop(); let tempEl = document.createElement('div'); tempEl = document.createElement('div'); this.el = document.createElement('div'); this.el.setAttribute('class', 'spin-mask'); tempEl.className = this.opts.className; tempEl.setAttribute('role', 'progressbar'); css(tempEl, { position: this.opts.position, width: 0, zIndex: this.opts.zIndex, left: this.opts.left, top: this.opts.top, transform: "scale(" + this.opts.scale + ")", });
drawLines(tempEl, this.opts); this.el.append(tempEl) console.log(this.el);
if (target) { target.insertBefore(this.el, target.firstChild || null); }
return this; }; /** * Stops and removes the Spinner. * Stopped spinners may be reused by calling spin() again. */ Spinner.prototype.stop = function () { if (this.el) { if (typeof requestAnimationFrame !== 'undefined') { cancelAnimationFrame(this.animateId); } else { clearTimeout(this.animateId); } if (this.el.parentNode) { this.el.parentNode.removeChild(this.el); } this.el = undefined; } return this; }; return Spinner; }());API参考 (https://ajaxorg.github.io/ace-api-docs/index.html)
尝试 (https://mkslanc.github.io/ace-playground/)
有关更详细的选项列表,请参阅Configuring-Ace wiki 页面。 (https://github.com/ajaxorg/ace/wiki/Configuring-Ace)
// pass options to ace.edit<script src="https://www.unpkg.com/ace-builds@latest/src-noconflict/ace.js"></script>
const editor = ace.edit(element, { mode: "ace/mode/javascript", theme: "ace/theme/eclipse", selectionStyle: "text", maxLines :最大行数})
// use setOptions method to set several options at once
editor.setOptions({//设置自动滚动autoScrollEditorIntoView: true,//如果选择为空,则复制/剪切整行,默认为falsecopyWithEmptySelection: true,
});
// use setOptions method
editor.setOption("mergeUndoDeltas", "always");
// some options are also available as methods e.g.
// to get the value of the option use
editor.getOption("optionName");
//获取value值editor.getSession().getValue()editor.getSession().setValue()
//:设置主题editor.setTheme("ace/theme/twilight");
//语言editor.session.setMode("ace/mode/javascript");(https://github.com/ajaxorg/ace/blob/master/src/ext/themelist.js)在运行时获取可用主题的列表。
- 默认情况下,编辑器支持纯文本模式。所有其他语言模式都可以作为单独的模块提供,按需加载,如下所示:
二、基本使用
Section titled “二、基本使用”Ace 仅在调整窗口大小时检查其容器大小的变化。如果您以其他方式调整编辑器 div 的大小,并且需要 Ace 调整大小,请使用以下命
令:
editor.resize()设置和获取内容:
editor.setValue("the new text here");
editor.setValue("text2", -1); // set value and move cursor to the start of the text
editor.session.setValue("the new text here"); // set value and reset undo history
editor.getValue(); // or session.getValue三、文本操作
Section titled “三、文本操作”// 获取选中文本editor.getSelectedText();
//获取值editor.getSession().getValue()
//获取某一个范围editor.session.getTextRange(editor.getSelectionRange());
//获取当前光标所在行和列:editor.selection.getCursor();
//获取总行数:editor.session.getLength();//在光标处插入,模拟用户输入:editor.insert("Something cool");
editor.session.insert({row: 0, column:0}, Date()+"");
//替换范围内的文本:editor.session.replace(new ace.Range(0, 0, 1, 1), "new text");//转到一行:editor.gotoLine(lineNumber);
//设置默认选项卡大小:editor.session.setTabSize(4);
//使用软选项卡:editor.session.setUseSoftTabs(true);
//设置字体大小:document.getElementById('editor').style.fontSize='12px';
//切换自动换行:editor.session.setUseWrapMode(true);
//设置行突出显示:editor.setHighlightActiveLine(false);
//设置打印边距可见性:editor.setShowPrintMargin(false);
//将编辑器设置为只读:
editor.setReadOnly(true); // false to make it editableeditor.find('needle',{ backwards: false, wrap: false, caseSensitive: false, wholeWord: false, regExp: false});editor.findNext();editor.findPrevious();您可以使用以下选项作为搜索参数:
needle:您要查找的字符串或正则表达式backwards:是否从光标当前位置向后搜索。默认为false.wrap:当搜索结束时是否将搜索回绕到开头。默认为false.caseSensitive:搜索是否应该区分大小写。默认为false.wholeWord:搜索是否仅匹配整个单词。默认为false.range:要搜索的范围。null将整个文档 设置为regExp:搜索是否为正则表达式。默认为false.start:开始搜索的 起始范围或光标位置skipCurrent:是否在搜索中包含当前行。默认为false.preventScroll:是否将光标移动到下一个匹配项。默认为false.
以下是执行替换的方法:
editor.find('foo');editor.replace('bar');这是全部替换:
editor.replaceAll('bar');监听onchange:
editor.session.on('change', function(delta) { // delta.start, delta.end, delta.lines, delta.action});监听变化selection:
editor.session.selection.on('changeSelection', function(e) {});监听变化cursor:
editor.session.selection.on('changeCursor', function(e) {});4、Fabricjs 拖动案例
Section titled “4、Fabricjs 拖动案例”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { background-color: #F2F5FA; } .containerCanvas { margin: auto;
} .file-upload { display: none; } nav { margin: auto; display: flex; width: 540px; justify-content: space-between; margin-bottom: 10px; } button { background-color: #1D84FD; color: aliceblue; padding: 8px 12px; border: none; border-radius: 150px; } </style></head><body> <input id="fileupload" class="file-upload" type="file" accept="image/*" name="picture"/> <nav> <button onclick="fileupload.click()">选择图片</button> <button class="downBtn">下载图片</button> </nav> <canvas height="500" width="540" id="canvasBox"></canvas></body><script src="https://unpkg.com/fabric@4.6.0/dist/fabric.min.js"></script><script src="../js/index.js"></script><script> fabric.Object.prototype.controls.mtr.withConnection = false; // 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形 // fabric.Object.prototype.cornerStyle = "circle"; // // 修改控制点的填充色为白色 // fabric.Object.prototype.cornerColor = "white"; // // 修改控制点的大小为10px // fabric.Object.prototype.cornerSize = 10; // // 设置控制点不透明,即可以盖住其下的控制线 // fabric.Object.prototype.transparentCorners = false; // // 修改控制点的边框颜色为`gray`灰色 // fabric.Object.prototype.cornerStrokeColor = "gray";
// // 单独修改旋转控制点距离主体的纵向距离为-20px // fabric.Object.prototype.controls.mtr.offsetY = -20; // fabric.Object.prototype.controls.mtr.cornerSize = 0; // // 单独修改旋转控制点,光标移动到该点上时的样式为`pointer`,一个手的形状 // fabric.Object.prototype.controls.mtr.cursorStyle = "pointer";
var canvas = new fabric.Canvas('canvasBox', { backgroundColor: '#FFFFFF', selectionLineWidth: 2, containerClass: 'containerCanvas' });
canvas.setBackgroundImage( '../assets/image/bgc.png', canvas.renderAll.bind(canvas), {5、jszip提取excel中的图片
Section titled “5、jszip提取excel中的图片”这个可以直接预览excel
https://view.officeapps.live.com/op/view.aspx?src=https://www.okkrep.com/quotation_1763540177337%20-%20%E5%89%AF%E6%9C%AC%20(2).xlsx
/* #ifdef APP-PLUS */ console.log('App Launch'); // 1. 检查本地存储中是否有 'appLaunched' 标记 const isFirstLaunch = uni.getStorageSync(APP_LAUNCHED);
if (!isFirstLaunch) { // 2. 如果没有标记,说明是首次启动 console.log('这是应用首次启动'); // 跳转重置页重新配置站点 uni.navigateTo({ url: `/pages/reset/reset` }); } else { // 应用不是首次启动 uni.setTabBarItem({ index: 2, visible: false }) }
/* #endif */
/* #ifdef H5 */ uni.setTabBarItem({ index: 2, visible: false }) /* #endif */提取图片
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><input type="file" id="fileInput"><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script><script>
document.getElementById('fileInput').addEventListener('change', function(event) {
/** * @date: 2025-11-25 * @description: 这里的zip包一定是全局的,后面操作都要基于这个zip实例 */ const zip = new JSZip(); const file = event.target.files[0]; const asyncList = [] extractImagesFromExcel(zip, file, async (map, content) => {
Object.keys(content.files).forEach((fileName) => { // 拿到图片文件 if (fileName.indexOf('xl/media/') === -1 ) return if (content.files[fileName].dir === true) return; asyncList.push(new Promise(async resolve => { const base64 = await zip.file(content.files[fileName].name).async('base64') resolve({fileName, base64}) })) })
const base64List = await Promise.all(asyncList); // 将base64格式化一下让浏览器读取 const formatImageList = await Promise.all(base64List.map(({fileName, base64}) => { return new Promise(async resolve => { let imgType = detectImageType(base64); let base64Img = await compressBase64(`data:image/${imgType};base64,${base64}`, imgType); const idx = fileName.indexOf('.') resolve({fileName: fileName.slice(0, idx), base64Img}) }) }));
// 根据文件名字生成map const imageMap = formatImageList.reduce((pre, val) => { pre[val.fileName] = val.base64Img return pre }, {})
Object.keys(map).forEach(key => { const imageKey = key.split(',')[2] // 这里拿到生成好的map添加文件名对应id,进行赋值,value是图片base64 if (imageMap[imageKey]) { map[key] = imageMap[imageKey]
const divEl = document.createElement('div'); const imgEl = document.createElement('img'); imgEl.style.width = '100px' imgEl.style.height = '100px' imgEl.style.border = '1px solid red' divEl.append(imgEl) imgEl.src = imageMap[imageKey]; document.body.append(divEl)
} }) console.log(map);
}) });
function extractImagesFromExcel(zip, file, callback) { /** * @date: 2025-11-25 * @description: 同步读取文件 */ zip.loadAsync(file).then(async (content) => { const map = {} const asyncList = [] // 找到 drawings 文件,里面包含图片和文件对应关系 Object.keys(content.files).forEach(async (fileName) => { if (fileName.indexOf('xl/drawings/') === -1 ) return; //去掉文件目录 if (content.files[fileName].dir === true) return; asyncList.push(zip.file(fileName).async("text")) });
const xmlList = await Promise.all(asyncList) xmlList.forEach(xmlContent => { /** * @date: 2025-11-25 * @description: 将读取的文本解析xml文件 */ const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlContent, "application/xml"); console.log(xmlDoc); // 这里你可以根据 XML 结构解析出图片的位置 // 例如,通过查找 <xdr:pic> 标签获取图像位置信息 const from = xmlDoc.getElementsByTagName("xdr:from"); for (let f of from) { // 提取位置和大小等信息 // 你需要根据实际 XML 结构做相应的调整 const col = f.getElementsByTagName("xdr:col")[0].textContent; const row = f.getElementsByTagName("xdr:row")[0].textContent; const picEl = f.parentElement.getElementsByTagName("xdr:pic")[0] // 获取图片id const rid = picEl.getElementsByTagName("a:blip")[0].getAttribute('r:embed'); // 整理出来包含位置和图片id的key,用于后续替换,value后续保存图片base64 map[`${parseInt(row)+1},${parseInt(col)+1},xl/media/image${rid.slice(3)}`] = '' } }) callback(map, content) }); }
/** * 图片格式检测器(支持PNG/JPEG) * @param {string} base64 - 包含DataURL前缀的Base64字符串 * @returns {'png' | 'jpeg'} */ function detectImageType(base64) { // 性能优化:仅解码前8个Base64字符(对应6字节) const dataPart = base64.indexOf(',') >= 0 ? base64.slice(base64.indexOf(',') + 1, base64.indexOf(',') + 9) : base64.slice(0, 8);
try { // 优化点:避免使用atob产生临时字符串 const byteArray = Uint8Array.from(atob(dataPart), (c) => c.charCodeAt(0));
// PNG检测:前8字节特征值 if ( byteArray.length >= 8 && byteArray === 0x89 && byteArray === 0x50 && byteArray === 0x4e && byteArray === 0x47 ) { return 'png'; }
// JPEG检测:前3字节特征值 if (byteArray.length >= 3 && byteArray === 0xff && byteArray === 0xd8 && byteArray === 0xff) { return 'jpeg'; } } catch (e) { console.warn('Base64解码失败:', e); } return 'png'; }
/** * @date: 2025-11-25 * @description: 获取图片大小 */ function getBase64ImageSize(base64) { // 移除URL中的前缀和数据后缀 const dataURI = base64.replace(/^data:image\/\w+;base64,/, ''); // 计算字节大小 const byteSize = dataURI.length * 0.75; // base64编码是3/4原始大小 return byteSize; }
/** * @date: 2025-11-25 * @description: 压缩zip */ function compressBase64( base64Image, imgType = 'jpeg', maxWidth = 4096, maxHeight = 4096, quality = 0.8, ) { return new Promise((resolve, reject) => { /* if (props.compressImg == false) { resolve(base64Image); return; }*/ const sizeInMB = getBase64ImageSize(base64Image) / (1024 * 1024); // 将字节转换为MB if (sizeInMB <= 10) { resolve(base64Image); return; } console.log('压缩前图片大小:' + sizeInMB + 'm'); const img = new Image(); img.src = base64Image;
// 等待图片加载完成 img.onload = () => { // 创建 canvas 元素 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d');
// 获取原始图片的宽高 const width = img.width; const height = img.height;
// 计算新的宽高,保持宽高比 let newWidth = width; let newHeight = height;
// 如果图片宽度超过最大宽度,进行缩放 if (width > maxWidth) { newWidth = maxWidth; newHeight = (maxWidth / width) * height; }
// 如果图片高度超过最大高度,进行缩放 if (newHeight > maxHeight) { newHeight = maxHeight; newWidth = (maxHeight / height) * width; }
// 设置 canvas 的尺寸 canvas.width = newWidth; canvas.height = newHeight;
// 将图片绘制到 canvas 上 ctx.drawImage(img, 0, 0, newWidth, newHeight);
// 将 canvas 转换为 base64 格式,指定质量 const compressedBase64 = canvas.toDataURL('image/' + imgType, quality);
console.log('压缩后图片大小:' + getBase64ImageSize(compressedBase64) / (1024 * 1024) + 'm');
// 返回压缩后的 base64 字符串 resolve(compressedBase64); };
// 处理图片加载错误 img.onerror = (err) => { reject('图片加载失败: ' + err); }; }); }
</script></body></html>