鼠标拖拽点击—电影院自定义布局
标签
鼠标拖拽、自定义布局
效果
自定义布局-电影院
目的
疫情期间,电影院都是隔空而坐,想用html
、css
、js
、jq
做个简单的自定义布局。
介绍
自定义布局中有银幕、列号、过道、座位和预留五个元素,可通过鼠标点击和鼠标拖拽选中格子,进行个性化定制
用法
- 首先输入布局的宽高
- 点击按钮,生成空白座位图
- 选择颜色块中的一个标识,进行定制
- 点击红色块,进行座位选中,可用单击可用鼠标拖动,按照拖动方向正序生成数字
- 点击紫色块,进行预留选中,可用单击选中
- 点击橙色块,进行座位和预留取消,可用单击可用鼠标拖动
- 点击粉紫块,进行第一行选中,可用单击选中取消
- 点击灰色块,进行过道选中,可用单击可用鼠标拖动
- 定制完成后,可点击按钮进行预览
代码
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义布局</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="templateLayout.css">
</head>
<body>
<div class="wrapper">
<div id="moveSelected"></div>
<div class="flexBox">
<div class="designBox">
<div class="title">疫情期间电影院自定义布局</div>
<!-- 布局长宽 -->
<div class="inputBox">
<p>
横: <input type="number" id="sideWidth" name="sideWidth" onchange="setWidth()" value="30">
纵: <input type="number" id="sideHeight" name="sideHeight" onchange="setHeight()" value="5">
</p>
<p>
是否显示:
<input type="radio" name="rostrum" onchange="setRostrum()" value="yes" checked>是
<input type="radio" name="rostrum" onchange="setRostrum()" value="no">否
</p>
</div>
<!-- 布局按钮 -->
<div class="btnBox">
<div>
<button onclick="createCanvas()">生成座位图</button>
<button class="preview" onclick="toPreview()">布局预览</button>
</div>
<div onclick="selectSeat()"><span class="selectBtn seat"></span>座位</div>
<div onclick="selectReverse()"><span class="selectBtn reverse"></span>预留</div>
<div onclick="cancelSelect()"><span class="selectBtn cancel"></span>取消</div>
<div onclick="selectColumn()"><span class="selectBtn column"></span>列号</div>
<div onclick="selectRoad()"><span class="selectBtn road"></span>过道</div>
</div>
</div>
<div class="operationBox">
<div class="title">使用说明</div>
<ol>
<li>首先输入布局的宽高</li>
<li>点击按钮,生成空白座位图</li>
<li>
<div>选择颜色块中的一个标识,进行定制</div>
<ol type="i" class="colorExplain">
<li>点击红色块,进行座位选中,可用单击可用鼠标拖动,按照拖动方向正序生成数字</li>
<li>点击紫色块,进行预留选中,可用单击选中</li>
<li>点击橙色块,进行座位和预留取消,可用单击可用鼠标拖动</li>
<li>点击粉紫块,进行第一行选中,可用单击选中取消</li>
<li>点击灰色块,进行过道选中,可用单击可用鼠标拖动</li>
</ol>
</li>
<li>定制完成后,可点击按钮进行预览</li>
</ol>
</div>
</div>
<!-- 布局内容 -->
<div>
<div class="ulBox">
<div class="rostrum">
银幕
<div class="line"></div>
</div>
</div>
<div class="ulBox">
<ul class="flexBox getAllLi columnList" onclick="setColumn(event)"></ul>
</div>
<div class="ulBox">
<ul class="ulList getAllLi flexBox" onmousedown="mouseDown(event)"></ul>
</div>
</div>
</div>
<script type="text/javascript" src="templateLayout.js"></script>
</body>
</html>
css
*{
margin: 0;
padding:0;
}
.wrapper{
padding:10px;
}
.title{
font-weight: bold;
font-size:24px;
}
.inputBox{
margin:10px 0;
}
.inputBox input[type='number']{
height:32px;
line-height: 32px;
outline: none;
border:1px solid #ddd;
padding-left:5px;
}
.inputBox p{
line-height: 32px;
}
ul li{
list-style: none;
}
.newBtna a{
display: inline;
background-color: transparent;
border: 0;
border-radius: 0;
color: $editBtnColor;
font-size: 17px;
padding: 8px 15px 8px 0;
// margin-right: 5px;
}
.version{
padding-top:3px;
}
.btnBox{
display: flex;
/*margin-left:20px;*/
align-items: center;
height:35px;
}
.btnBox button{
border:0;
outline: none;
color:#fff;
background-color: #2582EB;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
}
.btnBox button + button{
margin-right:20px;
}
.btnBox > div *{
vertical-align: middle;
}
.btnBox > div + div{
margin-left:20px;
}
.selectBtn{
display: inline-block;
width:35px;
height:35px;
margin-right:5px;
cursor: pointer;
}
.seat{
background:#F75A1C;
}
.reverse{
background: #C61CF7;
}
.cancel{
background:#F7B21C;
}
.column{
background:#F71CC9;
}
.ulBox{
overflow: auto;
}
.flexBox{
display: flex;
}
.designBox, .operationBox{
width: 50%;
}
.ulList,.columnList{
width:0px;
height:0px;
flex-wrap: wrap;
margin: 10px 0 0 30px;
}
.liItem{
width:29px;
height:29px;
margin:0 9px 9px 0;
border:1px solid pink;
cursor: pointer;
border-radius: 8px;
}
.selectedSeat{
background:#F75A1C;
color:#fff;
text-align:center;
line-height: 30px;
border-color: #F75A1C;
}
.selectedRemain{
background:#C61CF7;
color:#C61CF7;
border-color:#C61CF7 !important;
}
.selectedColumn{
background:#F71CC9;
color:#fff;
text-align:center;
line-height: 30px;
border-color:#F71CC9 !important;
}
.road{
background:#ddd;
color:#ddd;
text-align:center;
border-color:#ddd !important;
}
#moveSelected{
position: absolute;
width:0;
height:0;
left:0;
top:0;
opacity:0.2;
background:#000;
}
.version-record{
margin-top:30px;
}
.rostrum{
position: relative;
display: none;
/*background:#BCBCBC;*/
text-align: center;
line-height: 40px;
margin: 30px 0 0 30px;
overflow: hidden;
font-size:12px;
color:#aaa;
}
.line{
position: absolute;
width: 80%;
height:100%;
top:5px;
left:10%;
border-top:5px solid pink;
border-radius: 100%;
/*box-shadow: 1px 2px 3px pink;*/
}
.colorExplain{
padding-left:20px;
}
javascript
let sideWidth = 30 //画布宽
let sideHeight = 5 //画布长
let createFlag = false //生成画布标志
let liDomCollect = ''
let selectedSeatArr = [] //选择的座位(需要排序)
let selectColumnArr = [] //选择的列号(需要排序)
let selectType = ''//选择座位、预留、取消选择等
let dragFlag = false //鼠标拖动标志
let oldTop = '' // 鼠标点下位置
let oldLeft = '' // 鼠标点下位置
let moveSelected = $("#moveSelected") //鼠标拖动遮罩层
let dragDirection = 0 //拖动方向,左下、左上、右上、右下
let previewFlag = true //预览标志
let setFirstRow = '' //设置列号或者过道
let isFullCol = false //是否选满列号
let seat = ''//座位实际位置
let reserveseat = ''//预留实际位置
let seatamount = ''//座位+预留的实际位置数目
let seatnum = ''//座位和预留的按照顺序来序号,预留的都用0表示,座位会按照拖动顺序生成序号
let isHasRostrum = '0'//是否有主席台 (0无1有)
let isHasCol = '0'//是否有列号 (0无1有)
/**
* 是否显示银幕
*/
function setRostrum(){
let val = $("input[name='rostrum']:checked").val()
if(val == 'yes'){
$('.rostrum').css('display','block')
}else{
$('.rostrum').css('display','none')
}
}
/**
* 设置布局长度
*/
function setWidth(){
sideWidth = $('#sideWidth').val()
console.log(sideWidth)
}
/**
* 设置布局宽度
*/
function setHeight(){
sideHeight = $('#sideHeight').val()
console.log(sideHeight)
}
/**
* 生成画布
*/
function createCanvas(){
let wide = Number(sideHeight);
let long= Number(sideWidth);
if( !(wide > 0 && long > 0) ){
alert('请输入合适的数值')
return
}
$('.rostrum').css('display','block')
createFlag = true;
let numLi = long*wide
// 生成主席台
$(".rostrum").css({"width":long*40 - 9 + 'px',"height":'40px'})
// 生成列号
$(".columnList").css({"width":long*40 + 'px',"height":'40px'})
let columnLi = ""
let createColumn = long
while(createColumn>0){
columnLi += '<li class="liItem" data='+(long - createColumn + 1)+'></li>'
createColumn--
}
$(".columnList").html('')
$(".columnList").append(columnLi)
// 生成画布
$(".ulList").css({"width":long*40 + 'px',"height":wide*40 + 'px'});
let liDom = ""
let loopNum = numLi
while(numLi>0){
liDom += '<li class="liItem" data='+(loopNum - numLi + 1)+'></li>'
numLi--;
}
liDomCollect = liDom
$(".ulList").html('');
selectedSeatArr = [];
selectColumnArr = [];
$(".ulList").append(liDomCollect)
}
/**
* 设置列号,并对布局中选中的进行处理
*/
function setColumn(e){
if(e.target.tagName == 'UL'){
return
}
switch(setFirstRow){
case 'column':
if($(e.target).hasClass('road')){
$(e.target).removeClass('road');
clearColumn($(e.target).attr('data'),true);
}
if($(e.target).hasClass('selectedColumn')){
$(e.target).removeClass('selectedColumn');
$(e.target).text("");
removeItem(selectColumnArr,e.target);
sortSelectedArr(selectColumnArr);
}else{
let columnLen = selectColumnArr.length;
$(e.target).addClass('selectedColumn');
$(e.target).text(columnLen + 1);
selectColumnArr.push(e.target);
}
break;
case 'road':
let isClearRoad = false;
if($(e.target).hasClass('selectedColumn')){
$(e.target).removeClass('selectedColumn');
removeItem(selectColumnArr,e.target);
sortSelectedArr(selectColumnArr);
$(e.target).addClass('road');
$(e.target).text("-1");
}else if($(e.target).hasClass('road')){
$(e.target).removeClass('road');
$(e.target).text("");
isClearRoad = true;
}else{
$(e.target).addClass('road');
$(e.target).text("-1");
}
clearColumn($(e.target).attr('data'),isClearRoad)
break;
default :
alert("请选择列号或者过道")
break;
}
}
// 处理选中过道之后画布的清空操作
function clearColumn(realColumn,isClearRoad){
let columnNum = Number(sideHeight);
let width = Number(sideWidth);
let index = Number(realColumn);
let columnLiArr = [];
for(let i=0; i<columnNum;i++){
let findIndex = index + i*width
columnLiArr.push($(".ulList li[data="+findIndex+"]")[0]);
}
for(let j=0; j<columnLiArr.length; j++){
if(!isClearRoad){
$(columnLiArr[j]).addClass('road');
// 去掉预留
if($(columnLiArr[j]).hasClass('selectedRemain')){
$(columnLiArr[j]).removeClass('selectedRemain');
}
// 去掉座位
if($(columnLiArr[j]).hasClass('selectedSeat')){
$(columnLiArr[j]).removeClass('selectedSeat');
$(columnLiArr[j]).text("");
removeItem(selectedSeatArr,columnLiArr[j])
}
}
else{
$(columnLiArr[j]).removeClass('road');
}
}
sortSelectedArr(selectedSeatArr);
}
// 鼠标按下
function mouseDown(e){
console.log(e)
if(selectType === ''){
alert('请选择座位位置或者预留位置');
return;
}
//选中座位区域
console.log(moveSelected)
if(selectType === 'seat' || selectType === 'cancelFlag'){
dragFlag = true;
oldTop = e.pageY;
oldLeft = e.pageX;
moveSelected.css({"top":oldTop+'px',"left":oldLeft+'px'});
e.preventDefault();
e.stopPropagation();
}
// 选择预留座位
if(selectType === 'reverse'){
// 当前为过道不可选
if($(e.target).hasClass('road')){
alert('过道不可选')
return;
}
let curTarget = $(e.target);
if(curTarget.hasClass("liItem")){
if(curTarget.hasClass("selectedSeat") && !(curTarget.hasClass("selectedRemain"))){
curTarget.removeClass("selectedSeat").addClass("selectedRemain");
curTarget.text("");
// this.selectedReverseArr.push(curTarget);
removeItem(selectedSeatArr,e.target);
sortSelectedArr(selectedSeatArr);
}else if(curTarget.hasClass("selectedRemain")){
// 删除已选预留
curTarget.removeClass("selectedRemain");
// this.removeItem(this.selectedReverseArr,e.target);
}else{
// 添加已选预留
curTarget.addClass("selectedRemain");
// this.selectedReverseArr.push(curTarget);
}
}
}
document.onmousemove = (e) => {
if(!dragFlag) return
if(selectType === 'seat' || selectType === 'cancelFlag'){
let newTop = e.pageY;
let newLeft = e.pageX;
let moveWidth = oldLeft - newLeft;
let moveHeight = newTop - oldTop;
// 四个方向 需要处理左下选中超过两行 右上选中超过两行
// 向左拖动
if(newLeft < oldLeft){
// that.moveSelected.css({"left":newLeft+'px',"width":moveWidth+'px'})
if(newTop > oldTop){
// 左下 需要处理
dragDirection = 2;
moveSelected.css({"height":moveHeight+'px',"left":newLeft+'px',"width":moveWidth+'px'})
}else{
// 左上
dragDirection = 1;
moveSelected.css({"top":newTop+'px',"height":(oldTop-newTop)+'px',"left":newLeft+'px',"width":moveWidth+'px'})
}
}
// 向右拖动
else{
// that.moveSelected.css({"width":(newLeft-that.oldLeft)+'px'});
if(newTop > oldTop){
// 右下 需要处理
dragDirection = 0;
moveSelected.css({"height":moveHeight+'px',"width":(newLeft-oldLeft)+'px'})
}else{
// 右上
dragDirection = 3;
moveSelected.css({"top":newTop+'px',"height":(oldTop-newTop)+'px',"width":(newLeft-oldLeft)+'px'})
}
}
}
}
// $(document).mousemove( )
document.onmouseup = (e) => {
if(selectType === 'seat' || selectType === 'cancelFlag'){
let right = moveSelected.width() + moveSelected.offset().left + 'px';
let bottom = moveSelected.height() + moveSelected.offset().top + 'px';
moveSelected.css({"right":right,"bottom":bottom});
findSelectLi();
dragFlag = false;
initmoveSelected();
e.preventDefault();
e.stopPropagation();
}
dragFlag=false;
moveSelected.css({"width":"0px","height":"0px","top":"0px","left":"0px"});
$(document).unbind("mousemove");
$(document).unbind("mouseup");
}
}
/**
* 预览功能
*/
function toPreview(){
if(previewFlag){
$("li").css("border-color","#fff");
$(".preview").text("取消预览");
}else{
$("li").css("border-color","pink");
$(".preview").text("生成预览");
}
previewFlag = !previewFlag;
}
/**
* 选择座位
*/
function selectSeat(){
selectType = 'seat';
}
/**
* 选择预留
*/
function selectReverse(){
selectType = 'reverse';
}
/**
* 取消选择
*/
function cancelSelect(){
selectType = 'cancelFlag';
}
/**
* 选择选择列号
*/
function selectColumn(){
setFirstRow = 'column';
}
/**
* 选择过道
*/
function selectRoad(){
setFirstRow = 'road';
}
// 选择选中的li
function findSelectLi(){
let selectedLi = [];
let prevLen = selectedSeatArr.length;
let blockLi = $(".ulList").find("li");
let len = blockLi.length
for(let i=0;i<len;i++){
// 获取方块位置
let left = $(blockLi[i]).offset().left;
let right = $(blockLi[i]).width() + left;
let top = $(blockLi[i]).offset().top;
let bottom = $(blockLi[i]).height() + top;
// 获取遮罩层的位置
let moveSelectedLeft = moveSelected.offset().left;
let moveSelectedRight = Number(moveSelected.css("right").split('px')[0]);
let moveSelectedTop = moveSelected.offset().top;
let moveSelectedBottom = Number(moveSelected.css("bottom").split('px')[0]);
if(right > moveSelectedLeft && bottom > moveSelectedTop && left < moveSelectedRight && top < moveSelectedBottom){
// this.cancelFlag ? $(blockLi[i]).removeClass("selectedSeat") : $(blockLi[i]).addClass("selectedSeat");
selectedLi.push(blockLi[i]);
}
}
if(selectType === 'seat'){
// 处于选中座位的状态下,不处于拖动取消的状态,如果拖动选择的格子包含已选格子,则无效
// 选中一个的时候
//let hasRoad = false;//判断是否包含过道
let repeatNum = 0;
let curSelectNum = selectedLi.length;
for(let i=0;i<curSelectNum;i++){
// 包含过道就无效,return终止循环
if($(selectedLi[i]).hasClass('road')){
return;
}
for(let j=0;j<selectedSeatArr.length;j++){
selectedSeatArr[j] == selectedLi[i] ? repeatNum++ : '';
}
}
// 点击一个已选格子,将其取消
if(curSelectNum === 1 && $(selectedLi[0]).hasClass("selectedSeat")){
$(selectedLi[0]).removeClass("selectedSeat");
$(selectedLi[0]).text("");
removeItem(selectedSeatArr,selectedLi[0]);
// 删除后重新排序
sortSelectedArr(selectedSeatArr);
return;
}
if(repeatNum >= 1 && curSelectNum > 1){
selectedLi = [];
}else{
switch(dragDirection){
// 右下,不需要处理
case 0:
selectedLi = processLi(selectedLi,prevLen);
break;
// 左上 逆序
case 1:
selectedLi.reverse();
selectedLi = processLi(selectedLi,prevLen)
break;
// 左下 需要处理
case 2:
selectedLi = processSpecialLi(selectedLi,prevLen);
break;
// 右上 需要处理
case 3:
selectedLi = processSpecialLi(selectedLi,prevLen);
break;
default:
break;
}
let prevArr = selectedSeatArr;
// console.log('处理之后的selectedLi',selectedLi);
selectedSeatArr = prevArr.concat(selectedLi);
}
}else{
// 判断取消座位,拖动选择的格子去掉类名和序号,并且已选格子重新排序。
let curSelectNum = selectedLi.length;
for(let i=0;i<curSelectNum;i++){
// 取消绿色格子并重新排序
if($(selectedLi[i]).hasClass("selectedSeat")){
$(selectedLi[i]).removeClass("selectedSeat");
$(selectedLi[i]).text("");
removeItem(selectedSeatArr,selectedLi[i]);
}
// 取消蓝色格子
if($(selectedLi[i]).hasClass("selectedRemain")){
$(selectedLi[i]).removeClass("selectedRemain");
// this.removeItem(this.selectedReverseArr,selectedLi[i]);
}
}
sortSelectedArr(selectedSeatArr);
}
}
// 从座位数组或者预留数组里面删除特定项
function removeItem(arr,item){
let arrLength = arr.length;
if(arrLength !== 0){
for(let i=0;i<arrLength;i++){
if(arr[i] == item){
arr.splice(i,1);
}
}
}
}
// 动态排序已选中座位
function sortSelectedArr(arr){
let arrLength = arr.length;
if(arrLength !== 0){
for(let i=0;i<arrLength;i++){
$(arr[i]).text(i+1);
}
}
}
// 处理
function processLi(arr,prevLen){
for(let i=0;i<arr.length;i++){
if($(arr[i]).hasClass("selectedRemain")){
$(arr[i]).removeClass("selectedRemain")
}
$(arr[i]).text(prevLen+i+1);
$(arr[i]).addClass("selectedSeat");
}
return arr;
}
//处理左上和右下方向拖动选中的li
function processSpecialLi(arr,prevLen){
let cutNum = 0;
let cutLen = 0;
let finalArr = [];
let loopLen = arr.length;
// 截取多少段
for(let i=0;i<loopLen-1;i++){
if($(arr[i]).attr("data") - $(arr[i+1]).attr("data") !== -1){
cutNum++;
}
}
// 每一段截取的长度
cutLen = parseInt(loopLen / (cutNum+1));
if(cutLen > Number(sideWidth)){
cutLen = Number(sideWidth);
}
// 获取每一行被截断的li数组
let cutArr = group(arr,cutLen);
// 如果鼠标往右上角拉动时,截取的cutArr需要做一个逆序的处理
if(dragDirection === 3){
cutArr.reverse();
}else{
// 否则是右下角拉动鼠标的时候,li数组内需要做一个逆序的处理
cutArr.forEach(function(value,index){
value.reverse();
})
}
cutArr.map(function(value,index){
finalArr = finalArr.concat(value);
})
let arrNeed = processLi(finalArr,prevLen);
return arrNeed;
}
function group(array, subGroupLength){
var index = 0;
var newArray = [];
while(index < array.length) {
newArray.push(array.slice(index, index += subGroupLength));
}
return newArray;
}
// 初始化moveSelected
function initmoveSelected(){
moveSelected.css({"width":"0px","height":"0px","top":"0px","left":"0px","bottom":"0px","right":"0px"});
}
// 获取要保存或者发布的数据
function obtainSaveData(){
let finalLi = $(".ulList").find("li");
let loopLen = finalLi.length;
let seatString = '';
let reverseString = '';
let total = 0;
let order = '';
// 获取的画布中的座位和预留
for(let i=0;i<loopLen;i++){
if($(finalLi[i]).hasClass("selectedRemain") || $(finalLi[i]).hasClass("selectedSeat")){
if($(finalLi[i]).hasClass("selectedRemain")){
order += "0";
reverseString += $(finalLi[i]).attr("data") + ',';
}else{
seatString += $(finalLi[i]).attr("data") + ',';
order += $(finalLi[i]).text();
}
total++;
order += ',';
}
}
// 获取列号和过道
if(showColumnRow){
isHasCol = '1';
let realCol = '';
let showCol = '';
let selectCol = 0;
let colLi = $(".columnList").find("li");
for(let i=0; i<colLi.length; i++){
realCol += $(colLi[i]).attr('data') + ',';
showCol += $(colLi[i]).text() + ',';
if($(colLi[i]).text() !== ''){
selectCol++;
}
}
if(selectCol == colLi.length){
isFullCol = true;
}
// this.formData.realCol = realCol.substring(0,realCol.length-1);
// this.formData.showCol = showCol.substring(0,showCol.length-1);
}
// this.formData.seat = seatString.substring(0,seatString.length-1);
// this.formData.reserveseat = reverseString.substring(0,reverseString.length-1);
// this.formData.seatamount = String(total);
// this.formData.seatnum = order.substring(0,order.length-1);
// this.showRostrum ? this.formData.isHasRostrum = '1': '';
// let sendData = this.formData
// return sendData;
}