网站首页背景代码,中国室内设计联盟网站,软件开发前景和收入,企业网站备案需要什么资料目录
一、上传
1.1、图片转base64
二、图片样式
2.1、图片边框【border-image】
三、Canvas
3.1、把canvas图片上传到服务器
3.2、在canvas中绘制和拖动矩形
3.3、图片(同色区域)点击变色
一、上传
1.1、图片转base64
传统上传#xff1a;
客户端选择图片#xf…目录
一、上传
1.1、图片转base64
二、图片样式
2.1、图片边框【border-image】
三、Canvas
3.1、把canvas图片上传到服务器
3.2、在canvas中绘制和拖动矩形
3.3、图片(同色区域)点击变色
一、上传
1.1、图片转base64
传统上传
客户端选择图片将图片上传到服务端等服务端返回一个url客户端再将url设置到图片的src里。图片在渲染时通过url去请求服务器服务端就会回馈url,客户端就可以看到预览图了。
优化上传
客户端选择图片后立刻展示然后继续上传到服务端保存他俩互不影响。 了解 url【统一资源定位符】: 协议://主机名[:端口号]/路径名[?查询字符串][#片段标识符]。 MIME【多用途互联网邮件扩展】: 指示数据的内容类型。 MIME类型内容表示含义文本类型text/html超文本标记语言用于网页图像类型image/pngPNG 图像格式支持透明度 音频类型audio/mpegMP3 音频格式 应用类型application/jsonJSON 数据格式用于数据交换多部分类型multipart/form-data用于 HTTP 表单数据特别是文件上传
示例
body!-- 运行后页面会弹出 alert(123)--script srcdata:application/javascript,alert(123)/script
/body
base64二进制数据转为ASCII 字符串 科普在js中 btoa() 函数用于将字符串进行 Base64 编码【btoa(alert(123))】 atob() 函数用于将 Base64 编码的字符串解码回原始字符串【atob(YWxlcnQoMTIzKQ)】 进入正题 bodyinput typefile /img src alt idpreview /script src./1.base64.js/script/body
const inp document.querySelector(input);
inp.onchange function () {const file inp.files[0];//多文件所以是数组const reader new FileReader();//创建了一个FileReader 对象用于读取文件内容reader.onload (e) {preview.src e.target.result;// e.target.result 以 Data URL 格式表示,并赋值console.log(file,转化后,e.target.result)};reader.readAsDataURL(file);//告诉FileReader 以 Data URL 格式读取文件内容
};
后端有时要FormData格式并添加其他参数而不是原始的二进制格式可以参考下 formatImage(type, file) {if (!this.fileUrl) {this.$message.warning(请上传图片)return false}for (let i 0; i 5; i) {const form new FormData()form.append(matting_type, i 1)form.append(hd_type, i 1)form.append(file, file)waterAxios.post(/oss/upload, form).then((res) {if (res.code 200) {this.$message.success(上传ok)}})}}, 二进制格式上传的消息格式application/octet-stream FormData格式上传的消息格式multipart/form-data 二、图片样式
2.1、图片边框【border-image】 stylebody {background-color: black;}.bdr-img {color: white;text-align: center;padding: 5rem;margin: 2rem auto;width: 50%;border: 50px solid #fff;border-image: url(./stamp.svg) 50 round;/* 相当于下面三行代码的组合 *//* border-image-source: url(./stamp.svg);border-image-slice: 50;border-image-repeat: round; */}/stylebodydiv classbdr-imgpHello, My name is [Your Name], and I am a [Your Profession] with [Numberof Years] years of experience in [Your Industry]. I specialize in [YourArea of Expertise] and have a strong background in [Relevant Skills orTechnologies]./p/div/body
三、Canvas
3.1、把canvas图片上传到服务器 let base64 canvas.toDataURL()//canvas指canvas格式的图片let imgUrlBlob dataURLToBlob(base64)var file new window.File([imgUrlBlob], image.png, { type: image/png })let fd new FormData()fd.append(image, file)
3.2、在canvas中绘制和拖动矩形 bodydivinput typecolor //divcanvas/canvasscript src./canvas.js/script/body
//canvas.js
const collorPicker document.querySelector(input);
const cvs document.querySelector(canvas);
const ctx cvs.getContext(2d);
function init() {const w 500,h 300;cvs.width w * devicePixelRatio;cvs.height h * devicePixelRatio;cvs.style.width w px;cvs.style.height h px;cvs.style.backgroundColor gray;
}
init();
const shapes [];
// 绘制矩形
// 矩形分为起始坐标和结束坐标最初结束坐标就是起始坐标结束坐标随着绘制发生改变
// 告诉canvas左上角是起始坐标确定最小值和最大值
class Rectangle {constructor(color, startX, startY) {this.color color;this.startX startX;this.startY startY;this.endX startX;this.endY startY;}//访问器属性get minX() {return Math.min(this.startX, this.endX);}get minY() {return Math.min(this.startY, this.endY);}get maxX() {return Math.max(this.startX, this.endX);}get maxY() {return Math.max(this.startY, this.endY);}draw() {ctx.beginPath();ctx.moveTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio); //左上角(起始坐标)ctx.lineTo(this.maxX * devicePixelRatio, this.minY * devicePixelRatio); //从左上角到右上角ctx.lineTo(this.maxX * devicePixelRatio, this.maxY * devicePixelRatio); //从右上角到右下角ctx.lineTo(this.minX * devicePixelRatio, this.maxY * devicePixelRatio); //从右下角到左下角ctx.lineTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio); //从左下角到左上角ctx.fillStyle this.color;ctx.fill(); //颜色填充ctx.strokeStyle #fff; //画笔颜色ctx.lineCap square; //线条交界处变圆润ctx.lineWidth 3 * devicePixelRatio; //画笔宽度ctx.stroke(); //完成边框的绘制}
}
// 自己随意画一个矩形
// const rect new Rectangle(red, 100, 100);
// rect.endX 200;
// rect.endY 200;
// rect.draw();
// 鼠标按下确定起始位置鼠标移动确定结束位置鼠标抬起结束事件
cvs.onmousedown (e) {const bouding cvs.getBoundingClientRect();const rect new Rectangle(collorPicker.value, e.offsetX, e.offsetY);// 进行判断const shape getShape(e.offsetX, e.offsetY);if (shape) {const { startX, startY, endX, endY } shape;const moveX e.offsetX;const moveY e.offsetY;window.onmousemove (e) {//拖动矩形const disX e.clientX - bouding.left - moveX;const disY e.clientY - bouding.top - moveY;shape.startX startX disX;shape.startY startY disY;shape.endX endX disX;shape.endY endY disY;};window.onmouseup () {window.onmousemove null;window.onmouseup null;};} else {shapes.push(rect); //将每个矩形数据加进去window.onmousemove (e) {rect.endX e.clientX - bouding.left;rect.endY e.clientY - bouding.top;};window.onmouseup () {window.onmousemove null;window.onmouseup null;};}
};
// 辅助函数判断鼠标按下时是否落在某个矩形内是执行移动 否执行新建矩形
function getShape(x, y) {// 从后往前遍历矩形数组找到最上面的那个矩形for (let i shapes.length - 1; i 0; i--) {if (x shapes[i].minX x shapes[i].maxX y shapes[i].minY y shapes[i].maxY) {return shapes[i];}}return null;
}
// 将shapes依次渲染出来
function draw() {requestAnimationFrame(draw);ctx.clearRect(0, 0, cvs.width, cvs.height); //画完清空一下for (const shape of shapes) {shape.draw();}
}
draw(); //初始化执行一次后续在每一帧里执行“画”这个动作前提数据shapes已经有了
3.3、图片(同色区域)点击变色 bodycanvas/canvasscript src./index.js/script/body
const cvs document.querySelector(canvas);
const ctx cvs.getContext(2d, { willReadFrequently: true }); //获取 Canvas 上下文function init() {const img new Image();img.onload () {cvs.width img.width;cvs.height img.height;ctx.drawImage(img, 0, 0, img.width, img.height);}; //当图片加载完成时:将图片绘制到画布上img.src ./redhat.png;
}
init(); //初始化时加载图片cvs.addEventListener(click, (e) {const x e.offsetX,y e.offsetY;// 1、获取点击位置的颜色: imgData.data就是目标对象所有的颜色信息const imgData ctx.getImageData(0, 0, cvs.width, cvs.height); //开始范围结束范围const clickColor getColor(x, y, imgData.data); //点击位置// 2、改变颜色const targetColor [46, 139, 87, 255]; // 改变后颜色为绿色透明度为不透明const visited new Set(); // 记录访问过的像素点changeColor(x, y, targetColor, imgData.data, clickColor, visited); //点击的像素点改变了ctx.putImageData(imgData, 0, 0);
});function pointIndex(x, y) {return (y * cvs.width x) * 4;
}function getColor(x, y, imgData) {const index pointIndex(x, y);return [imgData[index],imgData[index 1],imgData[index 2],imgData[index 3],]; //分别对应r、g、b、a
}// 使用BFS来代替递归
function changeColor(x, y, targetColor, imgData, clickColor, visited) {const queue [[x, y]]; // 用队列保存待处理的像素点const directions [[1, 0], [-1, 0], [0, 1], [0, -1]]; // 上下左右四个方向visited.add(${x},${y}); // 初始像素点标记为已访问while (queue.length 0) {const [cx, cy] queue.shift(); // 从队列中取出一个像素点const index pointIndex(cx, cy);const curColor getColor(cx, cy, imgData);// 如果颜色差异大于100或当前像素已经是目标颜色就跳过if (diff(clickColor, curColor) 100 || diff(curColor, targetColor) 0) {continue;}// 修改颜色imgData.set(targetColor, index);// 对周围的像素点进行处理上下左右for (const [dx, dy] of directions) {const nx cx dx, ny cy dy;// 检查边界if (nx 0 nx cvs.width ny 0 ny cvs.height) {const key ${nx},${ny};if (!visited.has(key) diff(clickColor, getColor(nx, ny, imgData)) 100) {visited.add(key); // 标记为已访问queue.push([nx, ny]); // 将该像素点加入队列}}}}
}function diff(color1, color2) {return (Math.abs(color1[0] - color2[0]) Math.abs(color1[1] - color2[1]) Math.abs(color1[2] - color2[2]) Math.abs(color1[3] - color2[3]));
} //计算颜色差异第三个案例总结
最初使用无穷递归来实现 // 递归找相同的像素点上下左右changeColor(x 1, y, targetColor, imgData, clickColor, visited);changeColor(x - 1, y, targetColor, imgData, clickColor, visited);changeColor(x, y 1, targetColor, imgData, clickColor, visited);changeColor(x, y - 1, targetColor, imgData, clickColor, visited);
但是导致了Maximum call stack size exceeded。最后使用广度优先搜索BFS来替代递归 优势 1使用队列实现BFS保存待处理的像素点避免递归带来的栈溢出 2逐层处理通过 queue.shift() 从队列中取出当前像素点检查它的上下左右四个方向并将符合条件的邻接像素点加入队列。 3避免重复访问通过 visited 集合避免重复访问已处理过的像素点。 ......待更新