想在同一位置显示交替显示图片时,可以利用鼠标单击事件,也可以利用鼠标移动事件来改变图片的src。
1 鼠标单击事件
<script type="text/javascript">
function ClickImg(indexedNum, name) {
// indexedNum可以是图片在网页中的序号,也可以是图片的name属性。
var n1 = name + ".jpg"
var n2 = name + "_2.jpg"
if (document.images[indexedNum].src.search(n1) != -1) {
document.images[indexedNum].src = n2
}
else {
if (document.images[indexedNum].src.search(n2) != -1) {
document.images[indexedNum].src = n1
}
}
} // 貌似不支持 document.getElementById(id)方法
</script>
2 鼠标移动事件
<SCRIPT language="JavaScript">
function movepic(img_name,img_src) {
document[img_name].src=img_src;
}
</SCRIPT>
<A HREF="http://www.javascript.com"
onmouseover="movepic('button','tu01.jpg')"
onmouseout="movepic('button','tu02.jpg')">
<IMG NAME="button" SRC="tu03.jpg" ALT="Image"></A>
3 单击超链接在同一区域加载不同图片和文本
load()
function load ( )
{
xImage=new Array (2)
xImage[0] = "pic01.gif"
xImage[1] = "pic02.gif"
xImage[2] = "pic03.gif"
xText=new Array (2)
xText[0] = "text 1"
xText[1] = "text 2"
xText[2] = "text 3"
}
function loadimage (x)
{
obr.src=xImage[x];
popis.innerText=xText[x]
}
html:
<a href="javascript: loadimage('0')">pic1</a>
<a href="javascript: loadimage('1')">pic2</a>
<a href="javascript: loadimage('2')">pic3</a>
<img src="pic01.gif" border="0" name="obr">
<div id="popis"></div>
4 下拉菜单图形预览
image1= new Image();
image1.src = "pic01.gif";
image2= new Image();
image2.src = "pic02.gif";
image3= new Image();
image3.src = "pic03.gif";
function imgchange(){
var imageNum = document.form.selImage.selectedIndex + 1;
fname = eval("image" + imageNum + ".src")
document.img.src = fname
}
html:
<form name="form">
<select name="selImage" size=1>
<option selected>图形 1
<option>图形 2
<option>图形 3
</select>
<input type="button" value="View" onClick="imgchange()" name="button">
</form>
<img src="pic01.gif" name="img" > </td>
5 多张图片通过单击“上一张”,下一张“交替显示
<html>
<head>
<script language="JavaScript">
num_of_slides = 5; // 图片数目
slide_num = 1; // 哪个图片首先显示
// 图片说明
desc1 = "我喜欢的007男主角";
desc2 = "我喜欢的007男主角";
desc3 = "我喜欢的007男主角";
desc4 = "我喜欢的007男主角";
desc5 = "我喜欢的007男主角";
function firstslide(){
slide_num = 1;
changeslide();
}
function prevslide(){
slide_num = slide_num - 1;
if(slide_num < 1){
slide_num = num_of_slides;
}
changeslide();
}
function nextslide(){
slide_num = slide_num + 1;
if(slide_num > num_of_slides){
slide_num = 1
}
changeslide();
}
function lastslide(){
slide_num = 5;
changeslide();
}
// 改变循环显示图片和相应的说明。
function changeslide(){
// 图片文件名pic1.jpg,pic2.jpg...
eval('document.picbox.src = "pic' + slide_num + '.jpg"');
eval('document.descform.descbox.value = "图片' + slide_num + '说明: '
+ eval('desc' + slide_num) + '"');
}
</script>
<title>Image Slideshow with description</title>
</head>
<!--不要忘记body里面的onload-->
<body onLoad="changeslide()" >
<br>
<div align="center"><br>
<br>
</div>
<div align="center">
<img src="pic1.jpg" name="picbox" width="120" height="155">
<form name="descform">
<p>
<textarea name="descbox" rows="3" cols="25" wrap="virtual" onFocus="this.blur()">
</textarea>
<br>
<a href="javascript:firstslide()">最前面</a>
<a href="javascript:prevslide()">上一幅</a>
<a href="javascript:nextslide()">下一幅</a>
<a href="javascript:lastslide()">最后面</a>
</p>
</form>
</div>
</body>
</html>
-End-
之前闲时,花了几个下班时间使用VUE+Spring Boot前后端分离做了一个博客,博客文章编辑采用vue-quill-editor插件,做了一些简单的配置后,就直接可用了,可惜在编辑文章时,默认的图片上传为Base64存放在文章内容中,这使得原本存储在数据库中的5这个大字段会因一篇文章的图片上传多而变得沉重。因为我使得的是云服务当服务器,有用过云服务的都知道,网络带宽很贵。在带宽只有3M的情况下,读取一篇几兆的文章会有几秒的延迟。一开始我以为是数据库读取问题,所以我把文章内容这个字段做了中间缓存,把常变字段(如阅读数)依然从数据库中读取,在逻辑上进行两者拼接返回给前端,但依然速度很慢。如下图,文章内容中的图片是BASE64存储,存储大小782KB,如果一篇文章的上传图片越多,也就会导致这个文章内容字段越大。
在不考虑提高带宽成本的情况下,准备把富文本图片默认存储为Base64的方案改成图片存储服务器,用URL引用,这样可以减少文章内容的读写IO。以下我将从引用富本文到图片改造再到后端接收上传的图片来介绍。
一、下载富文本依赖
npm install vue-quill-editor --save
下载完依赖后,在package.json中,将可以找到对应的版本。
二、在main.js中引用富文本,可以按Ctrl键+鼠标点击vue-quill-editor,看下是否可以看到源码,这样可以再次确认是否下载成功(我用的编译器是JetBrains WebStorm 2018.3.5 x64)
import VueQuillEditor from 'vue-quill-editor'
三、富文本的使用
1、HTML部分代码
<template> //中间省去其他内容,只显示富文本HTML <div class="edit_container"> <quill-editor v-model="blogsContent" ref="myQuillEditor" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange($event)"> </quill-editor> </div> </template>
2、JS部分代码
<script> import {quillEditor} from "vue-quill-editor"; //调用编辑器 import quillConfig from '../../api/quill-config.js'//重写富文本,使得图片走自定义上传 import 'quill/dist/quill.core.css'; import 'quill/dist/quill.snow.css'; import 'quill/dist/quill.bubble.css'; export default { name: 'editor', components: { quillEditor }, data() { return { editorOption: quillConfig,//重写 } } }
3、重写自定义图片上传quill-config.js代码
以下代码可以直接复制,然后改一下接口地址和你所要的参数即可
/*富文本编辑图片上传配置*/ const uploadConfig = { // 必填参数 图片上传地址 action: '/api/file/up/img', // 必填参数 图片上传接口地址 methods: 'POST', // 必填参数 图片上传方式 token: '', // 可选参数 如果需要token验证,假设你的token有存放在sessionStorage name: 'img', // 必填参数 文件的参数名 size: 500, // 可选参数 图片大小,单位为Kb, 1M = 1024Kb accept: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon' // 可选 可上传的图片格式 }; // toolbar工具栏的工具选项(默认展示全部) const toolOptions = [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{'header': 1}, {'header': 2}], [{'list': 'ordered'}, {'list': 'bullet'}], [{'script': 'sub'}, {'script': 'super'}], [{'indent': '-1'}, {'indent': '+1'}], [{'direction': 'rtl'}], [{'size': ['small', false, 'large', 'huge']}], [{'header': [1, 2, 3, 4, 5, 6, false]}], [{'color': []}, {'background': []}], [{'font': []}], [{'align': []}], ['clean'], ['link', 'image', 'video'] ]; const handlers = { image: function image() { var self = this; var fileInput = this.container.querySelector('input.ql-image[type=file]'); if (fileInput === null) { fileInput = document.createElement('input'); fileInput.setAttribute('type', 'file'); // 设置图片参数名 if (uploadConfig.name) { fileInput.setAttribute('name', uploadConfig.name); } // 可设置上传图片的格式 fileInput.setAttribute('accept', uploadConfig.accept); fileInput.classList.add('ql-image'); // 监听选择文件 fileInput.addEventListener('change', function () { // 创建formData,append这个方法是添加参数的方法,如果你的接口需要添加其他参数,比如用户ID,也可以在后面加进去。 var formData = new FormData(); formData.append(uploadConfig.name, fileInput.files[0]); formData.append('object','product'); // 如果需要token且存在token if (uploadConfig.token) { formData.append('token', uploadConfig.token) } // 图片上传 var xhr = new XMLHttpRequest(); xhr.open(uploadConfig.methods, uploadConfig.action, true); // 上传数据成功,会触发 xhr.onload = function (e) { if (xhr.status === 200) { // var res = JSON.parse(xhr.responseText);//如果你图片上传接口返回的是一个Json对象,就需要这个转,然后在下面获取图片URL let length = self.quill.getSelection(true).index; //这里很重要,你图片上传成功后,img的src需要在这里添加,res.path就是你服务器返回的图片链接。 //如果只是图片地址,在前端是显示不了的,需要添加前缀,你可以使用接口全路径,也可以使用我下面的VUE路由解决跨域问题 self.quill.insertEmbed(length, 'image','/api/file/get/img?url='+xhr.responseText); self.quill.setSelection(length + 1) } fileInput.value = '' }; // 开始上传数据 xhr.upload.onloadstart = function (e) { fileInput.value = '' }; // 当发生网络异常的时候会触发,如果上传数据的过程还未结束 xhr.upload.onerror = function (e) { }; // 上传数据完成(成功或者失败)时会触发 xhr.upload.onloadend = function (e) { // console.log('上传结束') }; xhr.send(formData) }); this.container.appendChild(fileInput); } fileInput.click(); } }; export default { placeholder: '', theme: 'snow', // 主题 modules: { toolbar: { container: toolOptions, // 工具栏选项 handlers: handlers // 事件重写 } } };
4、JAVA图片上传接口
@PostMapping("/up/img") @ApiOperation(value = "上传") public String upImg(HttpServletRequest request, @ApiParam(name = "userId", value = "id") String userId, MultipartFile img){ String fileName = ServiceCommon.ymdhmsdfStr.format(new Date())+"_"+img.getOriginalFilename();//防止图片重名覆盖,生成一个当前时间+图片名 String path = filePath;//配置文件中的图片存储前缀,如d://img/ String nameTypePath = "/" +userId+ "/blog_img" ;//中间路径 return upFileImgService.upFileService("/" + fileName, path + nameTypePath, nameTypePath, img);//接口看后面Service层 }
4.1、Service层
public Result upFileService(String fileName, String filePath, String nameTypePath, MultipartFile file) { try { File file1 = new File(filePath);//按路径生成文件对象 if(!file1.exists()){//如果文件不存在,将创建一个文件 file1.mkdirs(); } byte [] bytes = file.getBytes();//获取文件二进制字节数组 String url = filePath+fileName;//存储路径+文件名,产生一个文件存储地址 Path path = Paths.get(url); Files.write(path,bytes);//文件写入 return nameTypePath+fileName;//返回URL(我这个URL返回的前缀地址是截取掉的,不会显示IP等信息,只返回给前端后半地址,前端在调用图片获取时,将在接口中带入前缀,防止全路径暴露给前端) } catch (IOException e) { e.printStackTrace(); return ""; } }
5、图片获取接口
@GetMapping("/get/img") @ApiOperation(value = "获取图片") public void getImg(HttpServletResponse response, @ApiParam(name = "url", value = "文件路径") @RequestParam(required = true) String url, @ApiParam(name = "resType", value = "文件以什么类型输出(pdf,jpg)") @RequestParam(required = false) String resType) { String path = filePath + url; upFileImgService.download(path, response, "get", resType); }
5.1、图片获取接口Service层
public void download(String filePath, HttpServletResponse response,String type,String resType) { File file = new File(filePath); response.setContentLength((int)file.length()); try ( FileInputStream fis = new FileInputStream(file); OutputStream os = response.getOutputStream()) { byte[] buf = new byte[1024 * 256]; int i; while ((i = fis.read(buf)) != -1) { os.write(buf, 0, i); } os.flush(); } catch (IOException e) { System.out.println(e.getMessage()); } }
到此VUE富文本编辑器+Spring Boot接口就完成了。代码基本复制改改就可以使用。最后看下效果如何,如下图上,从原来的Base64内容变成URL地址,整个文章字段就缩小几十倍,这在IO读取时也加快了几十倍。
小白,昨天我们了解了input为file类型时选择图片的功能,我们暂且称它为inputfile吧,今天我们说一下javascript对inputfile的一些操作,为我们做图片上传做个准备。”
小白:“好啊!真想知道js是怎么读取文件信息的!”
老朱:“这是一个图片上传简单布局。”
“主容器main里面包含了inputfile、缩略图容器和上传按钮。现在我们要做的第一步功能就是,如何选择图片以后把图片放到缩略图容器中进行预览!”
老朱继续说道:“首选我们需要在用户选择文件以后,通过javascript拿到图片的信息!这就需要用到onchange事件和javascript的file对象知识。”
“这里用jQuery的change方法来检查inpufile内容是否发生了改变,一旦改变就从控制台输出当前选择的文件信息。从输出的结果你大概能理解file对象的构造,file对象包含了最后修改时间(lastModifiedDate)、文件名(name)、文件大小(size)、文件类型(type)等信息。至于输出的时候为什么要用this而不是$(this),这是因为file对象是js原生对象,跟jquery没关系,所以直接用this。”
小白:“我看这几个属性没有包含文件内容的属性啊!”
老朱:“恩,file对象只能拿到文件的信息,但是取不到文件的内容,我们还需要用一个H5新增的文件读取类FileReader,通过它去读file就能拿到文件的内容了!”
“实例化一个fileReader对象fr,通过fr的readAsDataURL方法去读file对象就能拿到图片的base64编码。”
小白:“图片的base64编码是什么呢?”
老朱:“简单理解就是把图片内容用一个特殊的文本编码呈现出来,图片进行base64编码以后我们就可以通过字符串来表示一个图片了。你可以自己去网上查查相关的介绍。”
老朱继续说道:“我们要拿到图片的base64编码需要等待读取完成以后才能获取,所以这是一个异步操作,因此需要用到onload事件。”
“输出fr对象的信息,我们可以看到result属性里面包含了图片的base64编码,所以现在只需将fr的result属性值赋值给一个img标签的src属性就可以看到这张图片。”
“注意这里呈现图片的时候并不是用的图片地址,而是用的图片的base64编码。通过这种方式我们就可以实时预览当前选择的图片了。”
小白:“为什么不直接使用图片的本地路径呢?”
老朱:“有时由于安全沙箱限制,本地图片是不能直接在网页上显示的!”
小白:“哦!我完了也去了解一下这方面的介绍。”
老朱:“通过上面的方法我们拿到了图片的base64编码,现在只需要一个能够接收base64编码的PHP页面就可以把图片存到服务器上了,这块功能我们明天再讨论,你先把今天说的东西练习练习。”
小白:“好的,我还有一个不明白的地方!选择文件的时候除了图片还有其他文件格式吧!这个怎么做区分呢?”
老朱:“我们刚开始不是输出了file对象的type和size属性了么?通过type属性可以判断当前文件是不是图片,通过size属性可以判断图片大小是不是超标。这是我们在实际开发的时候非常重要的两个属性,你一定要记住。好了你去练习吧,注意用好这两个属性(假如用户选择了非图片格式或者特别大的图片你应该怎么处理)。”
想学H5的朋友可以关注老炉,您的关注是我持续更新《小白HTML5成长之路》的动力!
*请认真填写需求信息,我们会在24小时内与您取得联系。