在前端添加水印的方式
- 使用canvas添加
- 通过canvas绘制单个水印
- 设置为某元素的背景图
- 并且通过MutationObserver监听该元素,确保背景图不会被手动关掉。
<canvas id = "canvas"></canvas>
function setWaterWrapper(text) {
const canvas = document.getElementById('canvas');
canvas.width = 200;
canvas.height = 200;
canvas.style.display = 'none';
const ctx = canvas.getContext('2d');
ctx.font = '30px';
ctx.fillStyle = 'rgba(200,44,99,0.7)'
ctx.rotate(-0.3);
ctx.fillText(text, canvas.width / 8, canvas.height / 2);
const img = canvas.toDataURL('image/png');
const style = `background-image:url(${img})`;
return style;
}
let style = setWaterWrapper('水印水印')
let box = document.getElementById('box');
box.setAttribute('style',style)
function callback() {
if (style!== box.style) {
box.setAttribute('style',style)
}
}
const observer = new MutationObserver(callback)
const targetNode = box;
const config = { attributes: true };
observer.observe(targetNode, config);
- 通过svg添加水印
- 给图片添加水印:使用canvas重新绘制图片,并输出图片url
- 通过上层覆盖一个dom元素,position:fixed
实现每隔一秒打印一次时间,时间格式为YYYY-MM-DD 00:00:00
- 实现要点
- 格式,如果是9月,date.getMonth()打印出来是9;
- < date.getMonth().toString().padStart(0,'2')> 通过转换可以使打印出来是09
- 代码实现
function getTime() {
let date = new Date();
let month = (date.getMonth() + 1).toString().padStart(2, '0')
let year = date.getFullYear();
let day = date.getDate().toString().padStart(2, '0');
let hours = date.getHours().toString().padStart(2, '0');
let min = date.getMinutes().toString().padStart(2, '0');
let sec = date.getSeconds().toString().padStart(2, '0');
console.log(year + '-' + month + '-' + day + ' ' + hours + ':' + min + ':' + sec)
}
let timer = setInterval(() => {
getTime()
}, 1000);
clearInterval(timer)
实现圣杯布局
- 什么是圣杯布局
- 有header,footer。中间部分container包含三个区域,left/center/right
- 其中left和right宽度是固定的,center的宽度是自适应的
- 使用float和margin负实现
- 实现要点
- 1、container中间的每一个元素都要设置为浮动元素
- 2、footer设置取消浮动,避免上移
- 3、container设置左右padding,为left和right预留出位置
- 4、left的margin设置-100%,并且设置定位到指定位置
- 5、right的margin设置负距离上移到container
<body>
<div class="header"></div>
<div class="container">
<div class="center column">中</div>
<div class="left column">左</div>
<div class="right column">右</div>
</div>
<div class="footer"></div>
</body>
<style>
.header {
background-color: aquamarine;
height: 20px;
}
.footer {
background-color: antiquewhite;
height: 20px;
clear: both;
}
.container {
padding-left: 200px;
padding-right: 300px;
background-color: rgb(173, 208, 239);
}
.container .column {
height: 500px;
float: left;
}
.left {
position: relative;
left: -200px;
margin-left: -100%;
width: 200px;
background-color: #bfa;
}
.right {
margin-right: -300px;
width: 300px;
background-color: beige;
}
.center {
width: 100%;
background-color: blanchedalmond;
}
</style>
- 使用flex实现
- 实现要点
- container设置display:flex
- 将center的flex设置为1,表示自适应占满
<style>
.header{
background-color: aliceblue;
}
.footer{
background-color: aqua;
}
.container{
height: 500px;
display: flex;
}
.center{
flex: 1;
background-color: azure;
}
.left{
width: 200px;
background-color: bisque;
}
.right{
width: 300px;
background-color: aquamarine;
}
</style>
- 使用grid实现
- 实现要点
- container的display:grid
- grid-template-areas:
"head head head head"
"left center center right"
"foot foot foot foot";
- 指定每哪一个是head,哪一个是foot、center、left、right
<style>
.header{
background-color: aliceblue;
}
.footer{
background-color: antiquewhite;
}
.left{
grid-area: left;
background-color: aqua;
}
.center{
grid-area: center;
background-color: beige;
}
.right{
grid-area: right;
background-color: bisque;
}
.container{
display: grid;
grid-template-areas:
"head head head head"
"left center center right"
"foot foot foot foot";
}
</style>
- 还用grid实现
- 实现要点
- 设置container的display:grid
- grid-template-columns: 1fr 2fr 1fr;每一列所占的宽度
<style>
.header{
background-color: aliceblue;
}
.footer{
background-color: antiquewhite;
}
.left{
background-color: aqua;
}
.center{
background-color: beige;
}
.right{
background-color: bisque;
}
.container{
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
</style>
使用proxy实现简单响应式对象
- proxy
- proxy可以代理一个对象
- 不仅能够代理对象的读写操作,还可以代理增删
- 使用Reflect中的方法操作对象。好处是不会报错而是返回操作成功与否的结果
- 代码实现
let person = {
name:'lxy',
pig:'hsh'
}
let p = new Proxy(person,{
get(target,propName){
console.log(`读取${propName}`,target,propName)
return Reflect.get(target,propName)
},
set(target,propName,value){
console.log('修改或者增加',target,propName,value)
Reflect.set(target,propName,value)
},
deleteProperty(target,propName){
console.log('删掉了')
return Reflect.deleteProperty(target,propName)
}
});
设计模式
单例设计模式
- 什么是单例设计模式
- 单例设计模式就是一个类仅能够创建一个实例,并且能够提供一个访问它的方法。
- 有哪些单例设计模式?
- 例如windows回收站、任务管理器。这种每次只能够打开一个的。
- 比如每次点击登录按钮,无论点击多少次登录按钮,都只会弹出一次登录框
- 像Vuex,jquery,lodash等第三方库都是单例模式,每次引用或者install都只会引用一次。
- 实现简单的单例设计模式
function Single(fn) {
let res = null
return function () {
return res == null ? (res = fn()) : res;
}
}
function createLogin() {
let div = document.createElement('div');
div.innerHTML = '这是一个登陆弹窗,只出现一次';
div.style.display = 'none';
document.body.append(div);
return div;
}
let res = Single(createLogin);
let btn = document.querySelector('button');
btn.addEventListener('click', () => {
let login = res();
login.style.display = 'block'
})
- 使用class实现:利用class中的静态类创建实例,如果实例有被创建,就返回已经创建的实例
class SingleClass{
constructor(name,creator,products){
this.name = name;
this.creator = creator;
this.products = products;
}
static getInstance(name,creator,products){
if(!this.instance){
this.instance = new SingleClass(name,creator,products)
}
return this.instance
}
}
let apple = SingleClass.getInstance('apple','a','iphone');
let copy = SingleClass.getInstance('applecopy','b','bphone');
console.log(apple, copy)
工厂模式
- 工厂模式
- 是用于创建对象的一种常用的设计模式。只需要传入参数就可以不断的生产出实例对象。
代理模式
- 当用户不方便直接访问一个对象时,提供一个代理对象代理对这个对象的全部属性。用户实际上是访问的代理对象
- 缓存代理:为一些开销大的运算结果提供暂时性的存储。下次运算时,如果传递进来的参数和之前一致,可以直接返回存储的计算结果。
- 虚拟代理:把一些开销很大的对象,延迟到真正需要的时候再去创建。
观察者模式,发布订阅模式
如何将数组结构转换为树结构?
- 应用场景:在配合后端进行开发时,后端返回的数据是一个数组,需要按照列表中的父子关系转换为树结构进行展示
- 从后端获取的数据列表:
let data = [
{ id: 0, parentId: null, name: '生物' },
{ id: 1, parentId: 0, name: '动物' },
{ id: 2, parentId: 0, name: '植物' },
{ id: 3, parentId: 0, name: '微生物' },
{ id: 4, parentId: 1, name: '哺乳动物' },
{ id: 5, parentId: 1, name: '卵生动物' },
{ id: 6, parentId: 2, name: '种子植物' },
{ id: 7, parentId: 2, name: '蕨类植物' },
{ id: 8, parentId: 4, name: '大象' },
{ id: 9, parentId: 4, name: '海豚' },
{ id: 10, parentId: 4, name: '猩猩' },
{ id: 11, parentId: 5, name: '蟒蛇' },
{ id: 12, parentId: 5, name: '麻雀' }
]
- 将数组转化为树结构的思路
- 如要检查传入的数据是否为数组类型
- 遍历每条数据,存到一个对象中,对象的key为id,value为每条数据
- 遍历每条数据,取出对象中存在parentid的每条数据,
- 如果存在parentid,就在相应的每条数据下添加children,为当前item
- 如果没有parent,就在结果中添加当前的数据。
function transTree(data) {
let res = [];
let map = {};
if (!Array.isArray(data)) return []
data.forEach(item => {
map[item.id] = item;
})
data.forEach(item => {
let parent = map[item.parentId]
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
res.push(item);
}
})
return res
}
transTree(data)
面试题:如果给你一个项目你该怎么做?
- 根据项目需求,确定项目类型
- 评估项目中的风险和可行性
- 根据自身和团队给出估时和排期,还有所需依赖的资源
- 设计整体架构,分发模块到相关人员
- 沟通和反馈,定期同步进度。
- 测试,优化,总结。