6月16日~29日任务进度及所有工作中遇到的困难
一、 网页前端开发
- z轴顺序问题
如果不定义z-index,同级元素按出现先后顺序来决定层级关系,后定义的元素,会层级更高,体现在如果出现在同一位置后出现的会覆盖先出现的。但是如果hover动画中定义元素的移动,在移动过程中元素的层级关系会发生变化,具体怎么变化并不清楚,但是我们可以在hover里定义他的z-index这样就不会出现以上状况。 - 图片回显
图片上传回显有两种方式:
1、上传图片之后存到服务器上,返回一个储存的路径回来就可以了。
这种方法很简单,但是多了一次和服务交互,不够优雅。
2、我们利用h5的特性用新增的filereader将input得到的file转成dataurl再将图片显示到页面上。
var fr = new FileReader();
var myFile = document.getElementById('file');
fil = myFile.files[0];
fr.readAsDataURL(fil);
fr.onload = function (e) {
$("#hxian").css("display", "block");
$("#hximg").attr("src", e.target.result);
$(".fake").css("display", "none");
$("#tix").css("display", "none");
}
这里有个小bug,但是如果两次上传的图片一样的话不会触发onload,只要记录一下上次的dataurl就行。
- 文件拖拽上传
禁止浏览器打开文件行为
document.addEventListener("drop",function(e){ //拖离
e.preventDefault();
})
document.addEventListener("dragleave",function(e){ //拖后放
e.preventDefault();
})
document.addEventListener("dragenter",function(e){ //拖进
e.preventDefault();
})
document.addEventListener("dragover",function(e){ //拖来拖去
e.preventDefault();
})
拖拽,预览文件
var box = document.getElementById('drop_area'); //拖拽区域
box.addEventListener("drop",function(e){
var fileList = e.dataTransfer.files; //获取文件对象
//检测是否是拖拽文件到页面的操作
if(fileList.length == 0){
return false;
}
//拖拉图片到浏览器,可以实现预览功能
//规定视频格式
Array.prototype.S=String.fromCharCode(2);
Array.prototype.in_array=function(e){
var r=new RegExp(this.S+e+this.S);
return (r.test(this.S+this.join(this.S)+this.S));
};
var video_type=["video/mp4","video/ogg"];
//创建一个url连接,供src属性引用
var fileurl = window.URL.createObjectURL(fileList[0]);
if(fileList[0].type.indexOf('image') === 0){ //如果是图片
var str="<img width='200px' height='200px' src='"+fileurl+"'>";
document.getElementById('drop_area').innerHTML=str;
}else if(video_type.in_array(fileList[0].type)){ //如果是规定格式内的视频
var str="<video width='200px' height='200px' controls='controls' src='"+fileurl+"'></video>";
document.getElementById('drop_area').innerHTML=str;
}else{ //其他格式,输出文件名
//alert("不预览");
var str="文件名字:"+fileList[0].name;
document.getElementById('drop_area').innerHTML=str;
}
resultfile = fileList[0];
},false);
二、chrome插件开发
插件开发的思路,右键点击图片的时候出现菜单,有选项识别图片的选项,识别时直接掉用上面写网页时用的接口,将识别结果复制到粘贴版上。
- chrome插件开发简单介绍
manifest.json:这是一个Chrome插件最重要也是必不可少的文件,用来配置所有和插件相关的配置,必须放在根目录。其中,manifest_version、name、version3个是必不可少的,description和icons是推荐的。
content-scripts:其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css的),借助content-scripts我们可以实现通过配置的方式轻松向指定页面注入JS和CSS(如果需要动态注入,可以参考下文),最常见的比如:广告屏蔽、页面CSS定制,等等。
background: 后台(姑且这么翻译吧),是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。 - chrome插件开发中遇到的问题
1.第一阶段,要解决的第一个问题是页面上的图片以file对象的形式发送给服务器。我们只需要获得图片的src,转换成canvas,再转成DataURL,再转成file对象。因为background.js可以随便跨域所以很轻松就传输上去了,但是这样子转换因为background.js是独立于原页面的,所以转换过程中相当于又向这个网页的服务器请求了一次图片,所以导致上传到服务器的图片和页面上的图片不一样。
var img = document.querySelector(img);
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var url = canvas.toDataURL('image/png');
var arr = url.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
var filename = "yzm.png";
var fil = new File([u8arr], filename, {
type: mime
});
var formData = new FormData();
formData.append("img", fil);
2.第二阶段,解决上传图片跟页面图片不一致的问题,通过同学了解到油猴插件,我放弃原生的方式开发插件,在油猴插件的基础上开发这个插件,油猴插件开发很简单,就只需要写一个js,并在js的开始按照油猴的标准写下注释就行,另外油猴还提供了很多很好用的自己带函数。在用油猴插件开发完成后,我发现页面上的图片转成dataurl之后在生成img和页面上的图片是一样的,但是又出现了另外一个bug就是无法发送ajax请求,如果在油猴里用ajax发送请求可能会出现跨域问题,所以我们可以使用油猴自己的http请求的函数,这样可以解决跨域问题,但是我出现的并不是跨域问题,而是在跨域请求之前的一个错误,不能从一个https页面的里发送一个http的请求,错误如图:
很神奇的是油猴也可以解决这个错误,成功的发送请求,但是在服务器端却无法响应请求,因为服务器处理不了https的请求,而我猜测那个自带的函数就是把http请求转成https请求所以才可以逃过服务器的安全机制。但是经过油猴的启发,我觉得可能是因为油猴是嵌入到页面的js所以可以把页面上的img转成dataurl之后还跟页面上一样的,因为油猴是嵌入的所以应该和页面都共享缓存,而转换的时候并不需要再次请求图片,而是直接从缓存中拿到图片就可以了。
3.第三阶段,我又从油猴开发换到了原生开发,在content-script上把img转成dataurl,然后再通过通信机制把dataurl传给background,再由background发送ajax请求,background.js发送ajax请求没有任何跨域问题所以我们就由它来发送,但是这里又出现一个问题就是chrome个个js之间通讯通道,存在的时间太短了,如果再里面放一个执行很长时间的函数,就会报上面的错。不过我们可以提前声明一个全局的Image变量让他load一下就行,这样就会触发onload函数,这个函数的执行实现不算在通道里面。以上就可以大致完成验证码识别的chrome插件了。
//background.js
chrome.runtime.onInstalled.addListener(createContextMenu);
chrome.runtime.onStartup.addListener(createContextMenu);
function createContextMenu() {
chrome.contextMenus.removeAll(function() {
chrome.contextMenus.create({
id: 'plan1',
title: '通道一识别',
contexts : ['image'],
});
chrome.contextMenus.create({
id: 'plan2',
title: '通道二识别',
contexts: ['image'],
});
});
}
var url;
var ans;
chrome.contextMenus.onClicked.addListener(function(info, tab) {
sendMessageToContentScript("验证码图片已经上传,正在识别!", function(response)
{
console.log('来自content的回复:'+response);
});
var arr = url.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
var filename = "yzm.png";
var fil = new File([u8arr], filename, {
type: mime
});
var formData = new FormData();
formData.append("img", fil);
$.ajax({
url: "http://xxx.xxxx.xxxx:xxxx/imgProcess/upload",
type: "post",
data: formData,
dataType: "json",
cache: false,
processData: false,
contentType: false,
success: function(data) {
if (data.code == 200) {
if(info.menuItemId=='plan2')
ans = data.imgCode2;
else
ans = data.imgCode1;
sendMessageToContentScript("识别成功,结果为:"+ans+",可以粘贴!", function(response)
{
console.log('来自content的回复:'+response);
});
const input = document.createElement('input');
document.body.appendChild(input);
input.setAttribute('value', ans);
input.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
console.log('复制成功');
}
document.body.removeChild(input);
} else
alert("分析失败!");
},
error: function(err) {
alert("请求失败!")
}
});
});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
url = request;
sendResponse("我已收到!");
});
function sendMessageToContentScript(message, callback)
{
chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
{
chrome.tabs.sendMessage(tabs[0].id, message, function(response)
{
if(callback) callback(response);
});
});
}
var Tip;
window.onload=function() {
Tip=AddTip();
$("img").each(function() {
$(this).on("contextmenu", function() {
var that=$(this);
var img = Aimed(that);
IdentifyResult = ConversionBase(img);
});
})
}
function Climb(Element, ElementLocalName, Joint = '') {
// console.log('上次过来值' + Joint);
var ElementType = (Element.id ? Element.id : Element.className ? Element.className.replace(/\s/g, ".") : false);
var Symbol = (Element.id ? "#" : Element.className ? "." : false);
if (ElementType && ElementLocalName == Element.localName) {
var Address = ElementLocalName + Symbol + ElementType;
} else {
var Address = Symbol + ElementType + ' ' + ElementLocalName;
}
// console.log('我叫' + Address);
// console.log('叫这个名字的有' + $(Address).length);
if ($(Address).length == 1) {
// console.log('只有一个' + Address);
// console.log('最后一次拼接' + Address + ' ' + Joint);
return Address + ' ' + Joint;
//我的名字唯一
} else {
// console.log(Element);
// console.log('这是我的父辈' + $(Element).parent()[0].className);
//让我的父辈去一辈一辈问我的老祖宗是谁
var Joint = this.Climb($(Element).parent()[0], $(Element).parent()[0].localName, Address + ' ' + Joint)
return Joint;
}
}
function ConversionBase(img) {
var img = document.querySelector(img);
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var url = canvas.toDataURL('image/png');
chrome.runtime.sendMessage(url, function(response) {
console.log(response);
});
}
function Aimed(Element) {
Element = Element[0];
var ElementLocalName = Element.localName;
var Symbol = (Element.id ? "#" : Element.className ? "." : false);
if (!Symbol) {
return Climb(Element.parentNode, ElementLocalName);
} else {
return Climb(Element, ElementLocalName);
}
}
function AddTip() {
var TipHtml = $("<div></div>").text("Text.");
TipHtml.css({
"background-color": "rgba(211,211,211,0.86)",
"display": "flex",
"align-items": "center",
"justify-content": "center",
"position": "fixed",
"color": "black",
"top": "-2em",
"height": "2em",
"margin": "0em",
"padding": "0em",
"font-size": "1.2em",
"width": "100%",
"text-align": "center",
"z-index": "999",
})
$("body").prepend(TipHtml);
return TipHtml;
}
function Hint(Content, Duration) {
Tip.stop(true, false).animate({
top: '-2em'
}, 300, function() {
Tip.text(Content);
});
Tip.animate({
top: '0em'
}, 500).animate({
top: '0em'
}, Duration ? Duration : 3000).animate({
top: '-2em'
}, 500)
return;
}
var timg=new Image;
var cht;
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
cht=request;
console.log(cht);
timg.src="https://www.baidu.com/img/flexible/logo/pc/result@2.png";
sendResponse('我收到了你的消息!');
});
timg.onload=function(){
Hint(cht,1500);
}