五星好评
产品需求
1.三组数据并行排列,鼠标放在文字上时后面的星星样式不变。
2.鼠标滑过,指针之前的星星变成红色的,鼠标离开,全部变成原样。
3.点击过后,鼠标离开星星,点击位置前的星星固定是红色的,鼠标再次滑过,点击位置之前的星星不影响,之后的星星随鼠标的滑过变成红色
4.鼠标滑过或点击,会有不同等级表情显示在星星的上方,鼠标离开,表情消失。
效果图:
默认样式
鼠标滑过 和 离开
鼠标点击和离开
星星和表情图片提供
分析
- 点击后,要把点击的数据发送出去,抛发事件
- 表情的移动位置是点击星星的位置
- 每组的星星小于等于滑过坐标,则为红色,每组的星星大于点击位置的则为原样(无色)
- 点击之后,小于点击位置的,是红色,否则是原样
js代码,封装组件
import Utils from "./Utils.js";
export default class FaceStar extends EventTarget{
label;
list=[]; // 把创建的星星存储到数组中,查找点击时的坐标用到
pos=-1; //和点击时的坐标进行比较
constructor(_label){
super();
this.label=_label;
this.elem=this.createElem();
}
createElem(){
if(this.elem) return this.elem;
let div=Utils.ce("div",{
height:"32px",
float:"left", //一共三组,并行排列,所以要浮动
marginRight:"20px",
position:"relative"
});
let span=Utils.ce("span",{ //文字容器
marginRight:"10px",
position:"relative",
top:"-3px"
});
span.textContent=this.label;
let con=Utils.ce("ul",{ // 星星容器
listStyle:"none",
margin:0,
padding:0,
height:"32px",
position:"relative",
display:"inline-block" //设置成行内块元素,可以和文本并行排列
})
for(var i=0;i<5;i++){ //创建星星
let star=Utils.ce("li",{
"16px",
height:"16px",
float:"left",
marginTop:"16px",
backgroundImage:"url(./img/commstar.png)"
});
this.list.push(star); //存储到数组
con.appendChild(star)
}
this.face=Utils.ce("div",{ //创建表情容器 块级元素上下排列
"16px",
height:"16px",
position:"absolute",
backgroundPositionX:-20*4+"px",
display:"none",
backgroundImage:"url(./img/face-red.png)"
})
con.appendChild(this.face);
div.appendChild(span);
div.appendChild(con);
con.addEventListener("mouseover",e=>this.mouseHandler(e)) //事件委托,用mouseover有冒泡触发
con.addEventListener("mouseleave",e=>this.mouseleaveHandler(e)) //离开时针对ul,不需要冒泡
con.addEventListener("click",e=>this.mouseHandler(e))
return div;
}
appendTo(parent){
if(typeof parent==="string") parent=document.querySelector(parent);
parent.appendChild(this.elem);
}
mouseHandler(e){
if(e.target.constructor!==HTMLLIElement) return;
var index=this.list.indexOf(e.target); //鼠标滑过星星时的坐标
if(e.type==="mouseover"){
this.face.style.display="block";
this.face.style.backgroundPositionX=(4-index)*-20+"px"
this.face.style.left=e.target.offsetLeft+'px'; //用星星的位置来让表情跟随移动
this.list.forEach((item,i)=>{
if(i<=index){
item.style.backgroundPositionY="-16px"
}else if(i>this.pos){ //小于点击时的位置不执行,则固定住了点击的位置
item.style.backgroundPositionY="0px"
}
})
}else if(e.type==="click"){
this.pos=index; //this.pos 是点击时候的位置
this.setStar(this.pos);
var evt=new Event("change");
evt.pos=this.pos;
this.dispatchEvent(evt);
}
}
mouseleaveHandler(e){
this.face.style.display="none";
this.setStar(this.pos)
}
setStar(index){ //点击位置之后的都是无色星星 可以点击小于固定位置的星星
this.list.forEach((item,i)=>{
if(i<=index){
item.style.backgroundPositionY="-16px"
}else{
item.style.backgroundPositionY="0px"
}
})
}
}
html页面,传递数据,接收FaceStar.js中传出来的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import FaceStar from "./js/FaceStar.js";
let arr=["快速包装","快递服务","送货上门"]
arr.forEach(item=>{
let face=new FaceStar(item);
face.appendTo("body");
face.addEventListener("change",changeHandler);
})
function changeHandler(e){
console.log(e.pos,this.label);
}
</script>
</body>
</html>
Utils.js是工具包
export default class Utils{
static time=0;
static ids=0;
static timeManage={};
//代码性能
static timeStart(){
if(Utils.time) return;
Utils.time=new Date().getTime();
}
static timeEnd(){
var t=new Date().getTime()-Utils.time;
Utils.time=0;
return t;
}
static ts(){
Utils.ids++;
Utils.timeManage[Utils.ids]=new Date().getTime();
return ids;
}
static te(id){
if(!Utils.timeManage[Utils.id]) return 0;
var t=new Date().getTime()-Utils.timeManage[Utils.id];
delete Utils.timeManage[Utils.id];
return t;
}
//随机颜色
static randomColor(){
var col="#";
for(var i=0;i<6;i++){
col+=Math.floor(Math.random()*16).toString(16);
}
return col;
}
//随机数值
static random(min,max){
return Math.floor(Math.random()*(max-min)+min);
}
//创建元素 设置样式 插入父级元素
static ce(type,style,parent){
var elem=document.createElement(type);
if(style){
for(var prop in style){
elem.style[prop]=style[prop];
}
}
if(typeof parent==="string") parent=document.querySelector(parent);
if(parent) parent.appendChild(elem);
return elem;
}
//读取css样式
static setStyle(styles){
var style=document.createElement("style");
document.head.appendChild(style);
var styleSheet=document.styleSheets[document.styleSheets.length-1];
for(var prop in styles){
Utils.addCss(styleSheet,prop,styles[prop]);
}
}
//添加css样式
static addCss(styleSheet,selector,style){
var str=selector+" {";
for(var prop in style){
var value=style[prop]
prop=prop.replace(/([A-Z])/g,function($1){
return "-"+$1.toLowerCase();
})
str+=prop+":"+value+";"
}
str+=" }";
styleSheet.insertRule(str,styleSheet.cssRules.length);
}
//css样式转化为js内样式
static CSStoString(str){
return str.replace(/(?<=:)(.*?)(?=;)|-[a-z](?=.+:)|;/g,function(item){
if(item===";") return ","
if(item[0]==="-") return item[1].toUpperCase();
return "'"+item.trim()+"'";
});
}
//字符串css转换为js内样式
static CSStoObject(str){
str=Utils.CSStoString(str);
return str.split(",").reduce((value,item)=>{
item=item.replace(/
/g,"");
var arr=item.split(":");
arr[0]=arr[0].replace(/s/g,"");
if(arr[1]===undefined) return value;
arr[1]=arr[1].replace(/'/g,"");
value[arr[0]]=arr[1];
return value;
},{})
}
}