JavaScript

语言:

JaveScriptBabelCoffeeScript

确定

var Grid = (function() {

'use strict';

var RAF = window.requestAnimationFrame;

var throttle = (function() {

var wait = false;

return function(callback, limit) {

if (!wait) {

callback();

wait = true;

setTimeout(function() {

wait = false;

}, limit);

}

}

})();

function Grid(settings) {

var that = this;

this.settings = $.extend({}, this.defaults, settings);

// calculate a grid's item width & height in percentages

//this.settings.rows = Math.ceil(this.settings.size / this.settings.columns);

this.settings.gridItemW = 100 / this.settings.columns,

this.settings.gridItemH = 100 / this.settings.rows;

this.DOM = {};

}

Grid.prototype = {

defaults: {

rows: 6,

columns: 12,

gap: 6, // in px

areaConstraints: {

minimum: [

[1, 1]

],

maximum: 10

},

areaTemplate: `

×

`

},

init() {

this.DOM.scope = $(this.generate());

this.DOM.items = this.DOM.scope.find('.grid__item');

this.events.binding.call(this);

return this;

},

generate() {

var item = `

grid = `

${Array(this.settings.rows + 2).join(item)}
${Array(this.settings.columns + 2).join(item)}

`;

return grid;

},

getCellCordsByIdx(absIdx) {

// return [(absIdx/this.settings.columns)|0, absIdx % this.settings.columns]; // [column, row]

return [absIdx % this.settings.columns, (absIdx / this.settings.columns) | 0]; // [row, column]

},

clearAllSelected() {

this.DOM.items.filter('.selected').removeClass('selected overlap');

},

selectCells(cells) {

this.clearAllSelected();

var regionOverlap = cells.filter('.defined').length > 0, // check all the cells if any are already "defined" (meanning they belong to an Area)

selectedClass = regionOverlap ? 'selected overlap' : 'selected';

// clear all previously selected cells

cells.addClass(selectedClass);

return !regionOverlap; // no overlap meanning it's a valid region selection

},

getAreaRange(startIdx, endIdx) {

return {

cols: [startIdx[0], endIdx[0]].sort((a, b) => a - b), // left most columns index, right most columns index

rows: [startIdx[1], endIdx[1]].sort((a, b) => a - b) // top most row index, bottom most row index

}

},

/**

* @param {Array} areaData - {rows:[MIN, MAX], cols[MIN, MAX]}

*/

getAreaCssPosition(areaData) {

return {

top: 'calc(' + areaData.rows[0] * this.settings.gridItemH + '%)',

left: 'calc(' + areaData.cols[0] * this.settings.gridItemW + '%)',

height: 'calc(' + (areaData.rows[1] - areaData.rows[0] + 1) * this.settings.gridItemH + '% - ' + this.settings.gap * 2 + 'px)',

width: 'calc(' + (areaData.cols[1] - areaData.cols[0] + 1) * this.settings.gridItemW + '% - ' + this.settings.gap * 2 + 'px)'

}

},

/**

* @param {Array} areaData - {rows:[MIN, MAX], cols[MIN, MAX]}

*/

setAreaProperties(areaElm, areaData) {

// position the template element

areaElm.css(this.getAreaCssPosition(areaData)).data({

'area': areaData

});

return this;

},

/**

* @param {Array} areaData - {rows:[MIN, MAX], cols[MIN, MAX]}

* @param {DOM Object} areaElm - optional - a predefined elmement that will rendered inside the template

*/

renderArea(areaData, areaElm) {

var tmpl = this.settings.areaTemplate;

areaElm = areaElm || '';

if (areaElm)

tmpl.replace('grid__area--empty', ''); // remove the "empty" modifier

areaElm = $(this.settings.areaTemplate).append(areaElm);

if (areaData)

this.setAreaProperties(areaElm, areaData);

return areaElm;

},

removeArea(areaElm) {

areaElm.addClass('removed');

setTimeout(() => {

areaElm.remove()

}, 500)

},

// detects an overlap of a givven area with others

detectOverlap(getMatches) {

var gap = this.settings.gap / 2,

areas = this.DOM.scope.find('> .grid__area:not(.grid__area--dragged)').map(function(idx) {

var rect = this.getBoundingClientRect();

return {

idx: idx,

elm: this,

x: [rect.left - gap, rect.left + rect.width + gap],

y: [rect.top - gap, rect.top + rect.height + gap]

};

}),

matches = [],

i, j;

// check one area against all others

for (i = 0; i < areas.length; i++)

for (j = 0; j < areas.length; j++) {

if (areas[i] !== areas[j] && // avoid checking the same area

areas[i].x[0] < areas[j].x[1] && areas[j].x[0] < areas[i].x[1] && // check X

areas[i].y[0] < areas[j].y[1] && areas[j].y[0] < areas[i].y[1]) { // check Y

if (getMatches)

matches[areas[i].idx] = areas[i].elm;

else

return [areas[i].elm];

}

}

return matches;

},

/**

* Given [x,y] coordinate on the grid, returns the cell (grid item) there

* @param {Array} cords [x, y] point in the whole grid

* @param {Array} gridOffset {top:[value], left:[value]}

*/

getGridPosByCords(cords, gridOffset) {

var xPos = cords.x - gridOffset.left,

xPercentage = xPos / this.DOM.scope[0].clientWidth * 100,

col = Math.floor(xPercentage / this.settings.gridItemW),

yPos = cords.y - gridOffset.top,

yPercentage = yPos / this.DOM.scope[0].clientHeight * 100,

row = Math.floor(yPercentage / this.settings.gridItemH);

// fix overflows

row = Math.min(row, this.settings.rows - 1);

col = Math.min(col, this.settings.columns - 1);

// fix negatives that might happen

row = row < 0 ? 0 : row;

col = col < 0 ? 0 : col;

return [col, row];

},

getItemSizeInPx() {

var someItem = this.DOM.scope.find('.grid__item');

return {

width: someItem.width(),

height: someItem.height()

}

},

events: {

binding() {

this.DOM.scope.on('mousedown.gridItem', this.events.callbacks.onGridItemMouseDown.bind(this))

.on('grid.selected', this.events.callbacks.onSelectedGrid.bind(this))

.on('mousedown', '.resize', this.events.callbacks.onAreaResizeMouseDown.bind(this))

.on('mousedown', '.grid__area__reposition', this.events.callbacks.onAreaRepositionMouseDown.bind(this))

.on('click.removeArea', '.grid__area__removeBtn', this.events.callbacks.onRemoveAreaBtnClick.bind(this))

},

callbacks: {

// Defining an Area

onGridItemMouseDown(e) {

// allow only left mouse click & only on the grid itself (not children)

if (e.which != 1 || !e.target.classList.contains('grid'))

return;

var that = this,

offset = this.DOM.scope.offset(),

startPos = this.getGridPosByCords({

x: e.clientX,

y: e.clientY

}, offset),

areaRange = this.getAreaRange(startPos, startPos),

selectionArea = this.renderArea(areaRange).addClass('grid__area--selection');

// append newly created area to the DOM

this.DOM.scope.append(selectionArea);

// if overlap occurs, remove the selectionArea completely

if (this.detectOverlap().length) {

selectionArea.remove();

return;

}

// bind "mouse move" event

this.DOM.scope.on('mousemove', onGridMouseMove);

$(document).on('mouseup.grid', onGridItemMouseUp.bind(this))

// on mouse-up callbacl

function onGridItemMouseUp(e) {

var overlap = selectionArea.hasClass('grid__area--invalid');

window.getSelection().removeAllRanges(); // fix any text selection (by "releasing" it) that might have ocur on the document

// clear events

$(document).off('mouseup.grid');

this.DOM.scope.off('mousemove');

if (overlap)

this.removeArea(selectionArea);

else

selectionArea.removeClass('grid__area--selection');

}

// on mouse-move callbacl

function onGridMouseMove(e) {

RAF(function() {

var endCellIdx = that.getGridPosByCords({

x: e.clientX,

y: e.clientY

}, offset);

areaRange = that.getAreaRange(startPos, endCellIdx);

// validSelection = this.selectCells(selectionCells.cells); // try to select the cells (if not overlapping occured)

// detect *any* overlap that might exist and set a class if so

selectionArea.toggleClass('grid__area--invalid', !!that.detectOverlap().length);

that.setAreaProperties(selectionArea, areaRange);

})

}

},

// Resizing

onAreaResizeMouseDown(mouseDownEvent) {

if (mouseDownEvent.which != 1) return;

var that = this,

resizeElm = $(mouseDownEvent.target), // the side-corner that was clicked on

areaItem = resizeElm.closest('.grid__area'), // area item

dir = mouseDownEvent.target.className.split('--')[1], // direction where dragging it allowed

boxArea = areaItem.data('area'), // get current area position

newBoxArea = $.extend(true, {}, boxArea), // new box area starts as the currently defined area position

lastValidRange,

mouseup = false, // flag on mouse up

minPosition = this.getAreaCssPosition({

cols: [0, 0],

rows: [0, 0]

}),

offset = this.DOM.scope.offset(); // grid offset cords

areaItem.addClass('grid__area--resized');

resizeElm.addClass('active');

this.DOM.scope.on('mousemove.resize', onResizeMouseMove);

// on mouse-up

$(document).on('mouseup.grid', function() {

mouseup = true;

window.getSelection().removeAllRanges(); // clear any window text selection

resizeElm.removeClass('active');

$(document).off('mouseup.grid');

that.DOM.scope.off('mousemove.resize');

var overlap = !!that.detectOverlap().length;

if (lastValidRange)

that.setAreaProperties(areaItem, lastValidRange);

areaItem.removeClass('grid__area--invalid grid__area--resized');

});

// on mouse-move

function onResizeMouseMove(e) {

RAF(function() {

if (mouseup) return;

var pos = that.getGridPosByCords({

x: e.clientX,

y: e.clientY

}, offset),

overlap,

tempPos,

areaRange;

// check constraints before updating positions

if (dir == 'top' && pos[1] <= boxArea.rows[1]) { // as long as Y position is lower or equal the original END Y position of the area

newBoxArea.rows[0] = pos[1];

if (newBoxArea.rows[1] == newBoxArea.rows[0]) {

areaItem[0].style.top = 'calc(' + newBoxArea.rows[0] * that.settings.gridItemH + '% + ' + that.settings.gap + 'px)';

areaItem[0].style.height = minPosition.height;

} else {

areaItem[0].style.top = e.clientY - offset.top - mouseDownEvent.offsetY - 2 + 'px';

areaItem[0].style.height = 'calc(' + (boxArea.rows[1] - boxArea.rows[0] + 1) * that.settings.gridItemH + '% + ' + (mouseDownEvent.clientY - e.clientY - that.settings.gap * 3) + 'px)';

}

}

if (dir == 'bottom' && pos[1] >= boxArea.rows[0]) { // as long as Y position is higher or equal the original START Y position of the area

newBoxArea.rows[1] = pos[1];

if (newBoxArea.rows[1] == newBoxArea.rows[0])

areaItem[0].style.height = minPosition.height;

else

areaItem[0].style.height = 'calc(' + (boxArea.rows[1] - boxArea.rows[0] + 1) * that.settings.gridItemH + '% + ' + (e.clientY - mouseDownEvent.clientY - that.settings.gap * 2) + 'px)';

}

if (dir == 'left' && pos[0] <= boxArea.cols[1]) { // as long as X position is lower or equal the original END X position of the area

newBoxArea.cols[0] = pos[0];

if (newBoxArea.cols[1] == newBoxArea.cols[0]) {

areaItem[0].style.left = 'calc(' + newBoxArea.cols[0] * that.settings.gridItemW + '% + ' + that.settings.gap + 'px)';

areaItem[0].style.width = minPosition.width;

} else {

areaItem[0].style.left = e.clientX - offset.left - mouseDownEvent.offsetX - 2 + 'px';

areaItem[0].style.width = 'calc(' + (boxArea.cols[1] - boxArea.cols[0] + 1) * that.settings.gridItemW + '% + ' + (mouseDownEvent.clientX - e.clientX - that.settings.gap * 3) + 'px)';

}

}

if (dir == 'right' && pos[0] >= boxArea.cols[0]) { // as long as X position is higher or equal the original START X position of the area

newBoxArea.cols[1] = pos[0];

if (newBoxArea.cols[1] == newBoxArea.cols[0])

areaItem[0].style.width = minPosition.width;

else

areaItem[0].style.width = 'calc(' + (boxArea.cols[1] - boxArea.cols[0] + 1) * that.settings.gridItemW + '% + ' + (e.clientX - mouseDownEvent.clientX - that.settings.gap * 2) + 'px)';

}

// that.setAreaProperties(areaItem, areaRange);

throttle(function() {

if (mouseup) return;

overlap = !!that.detectOverlap().length;

areaItem.toggleClass('grid__area--invalid', overlap);

if (!overlap) {

lastValidRange = that.getAreaRange([newBoxArea.cols[0], newBoxArea.rows[0]], [newBoxArea.cols[1], newBoxArea.rows[1]]);

}

}, 80);

})

}

},

// Re-positioning (via drag)

onAreaRepositionMouseDown(mouseDownEvent) {

if (mouseDownEvent.which != 1) return;

var that = this,

offset = this.DOM.scope.offset(),

areaItem = $(mouseDownEvent.target).closest('.grid__area'),

areaItemClone = $('

').attr('style', areaItem.attr('style')), // create a clone of the area with the same style (position)

boxArea = areaItem.data('area'),

newBoxArea = {

cols: 1,

rows: 1

},

lastAreaCords = [boxArea.cols[0], boxArea.rows[0]],

destPos,

areaSize = {

x: boxArea.cols[1] - boxArea.cols[0] + 1,

y: boxArea.rows[1] - boxArea.rows[0] + 1

},

mouseup = false;

areaItem.addClass('grid__area--dragged');

that.DOM.scope.addClass('dragging')

that.DOM.scope.append(areaItemClone);

// Mouse move & up Events

this.DOM.scope.on('mousemove.reposition', onAreaRepositionMouseMove);

$(document).on('mouseup.grid', onMouseUp);

// mouse up callback

function onMouseUp(e) {

mouseup = true;

$(document).off('mouseup.grid');

window.getSelection().removeAllRanges(); // fix any text selection (by "releasing" it) that might have ocur on the document

areaItem.removeClass('grid__area--dragged');

that.DOM.scope.removeClass('dragging').off('mousemove.reposition');

var selectionCells = that.getAreaRange(lastAreaCords, [lastAreaCords[0] + areaSize.x - 1, lastAreaCords[1] + areaSize.y - 1]),

overlap = areaItem.hasClass('grid__area--invalid'); // try to select the cells (if not overlapping occured)

areaItem.removeClass('grid__area--invalid');

if (overlap) {

areaItemClone.remove();

areaItem.addClass('grid__area--snapBack');

RAF(function() {

areaItem.css(that.getAreaCssPosition(boxArea));

// areaItem.css({ left:boxArea.cols[0] * that.settings.gridItemW + '%', top:boxArea.rows[0] * that.settings.gridItemH + '%' });

});

setTimeout(function() {

areaItem.removeClass('grid__area--snapBack')

}, 500);

} else {

RAF(function() {

that.setAreaProperties(areaItem, selectionCells);

});

setTimeout(function() {

areaItemClone.remove()

}, 300);

}

}

// mouse move callback

function onAreaRepositionMouseMove(e) {

RAF(function() {

if (mouseup) return; // make sure nothing will happen after "mouseup" was invoked

destPos = that.getGridPosByCords({

x: e.clientX,

y: e.clientY

}, offset);

var vaildColumnCords = destPos[0] + areaSize.x > that.settings.columns,

vaildRowCords = destPos[1] + areaSize.y > that.settings.rows,

xExceedsRight = areaItem[0].clientWidth + e.clientX - mouseDownEvent.offsetX > that.DOM.scope[0].clientWidth + offset.left,

yExceedsBottom = areaItem[0].clientHeight + e.clientY - mouseDownEvent.offsetY > that.DOM.scope[0].clientHeight + offset.top,

yExceedsTop = e.clientY - mouseDownEvent.offsetY - offset.top < 0,

areaPos = {

left: e.clientX - offset.left - mouseDownEvent.offsetX - 8 + 'px',

top: e.clientY - offset.top - mouseDownEvent.offsetY - 8 + 'px'

},

overlap = !!that.detectOverlap().length;

//

// position the "real" area

areaItem[0].style.left = areaPos.left;

areaItem[0].style.top = areaPos.top;

// detect *any* overlap that might exist and set a class if so

areaItem.toggleClass('grid__area--invalid', overlap);

// return the rummy to it's preior position

// areaItemClone.addClass('grid__area--snapBack').css( that.getAreaCssPosition(boxArea) );

areaItemClone.removeClass('grid__area--snapBack')

if (vaildColumnCords)

destPos[0] = that.settings.columns - areaSize.x;

if (vaildRowCords)

destPos[1] = that.settings.rows - areaSize.y;

// don't update clone styles if no change is needed

if (destPos[0] != lastAreaCords[0] || destPos[1] != lastAreaCords[1]) {

// snap the clone to allowed grid items

areaItemClone[0].style.left = 'calc(' + destPos[0] * that.settings.gridItemW + '%)';

areaItemClone[0].style.top = 'calc(' + destPos[1] * that.settings.gridItemH + '%)';

lastAreaCords = destPos;

}

if (overlap)

lastAreaCords = boxArea;

// freelly move the area itself (within the allowed grid)

// if( xExceedsRight )

// areaPos.left = that.DOM.scope[0].clientWidth - areaItem[0].clientWidth + 'px';

// if( yExceedsBottom )

// areaPos.top = that.DOM.scope[0].clientHeight - areaItem[0].clientHeight + 'px';

// if( yExceedsTop )

// areaPos.top = '0px';

});

}

},

// When an Area has been defined

onSelectedGrid(e, selectedGrid) {

// this.clearAllSelected();

// this.renderArea(selectedGrid);

},

// Removing (deleting) an Area

onRemoveAreaBtnClick(e) {

var areaElm = $(e.currentTarget).closest('.grid__area').addClass('removed');

this.removeArea(areaElm);

}

}

}

}

return Grid;

})();

//

$.when($.get("https://npmcdn.com/packery@2.0/dist/packery.pkgd.min.js"));

var grid = new Grid().init(),

packery;

$(document.body).append(grid.DOM.scope.addClass('editMode'));

///

// events

$('.pack').on('click', onPackBtnClick);

$('.clearAll').on('click', onClearAllBtnClick);

function onPackBtnClick() {

if (packery) {

packery.packery('reloadItems');

packery.packery('layout');

} else {

packery = grid.DOM.scope.packery({

itemSelector: '.grid__area',

resizeContainer: false,

percentPosition: true,

resize: false,

gutter: 1,

containerStyle: null

}).on('layoutComplete', function() {});

}

}

function onClearAllBtnClick() {

grid.DOM.scope.find('.grid__area').remove();

}

html5图片弹性布局,HTML5 使用弹性框布局实现可选择和压缩的网格相关推荐

  1. 3d布局图 html5,8个实用炫酷的HTML5图片动画应用 | HTML5资源教程

    近期我们发布了不少关于 1.CSS3/jQuery实现移动端滑动图片层叠效果 这是一款基于jQuery和CSS3的图片层叠插件,这和我们之前介绍的CSS3图片层叠展开特效有点不同,它支持鼠标滑动切换图 ...

  2. html5图片区域剪切,HTML5 本地裁剪图片并上传至服务器(老梗)

    很多情况下用户上传的图片都需要经过裁剪,比如头像啊什么的.但以前实现这类需求都很复杂,往往需要先把图片上传到服务器,然后返回给用户,让用户确定裁剪坐标,发送给服务器,服务器裁剪完再返回给用户,来回需要 ...

  3. html5图片灰度显示,HTML5 组件Canvas实现图像灰度化

    HTML5发布已经有很长一段时间了,一直以来从来没有仔细的看过,过年刚来随便看看 发现HTML5中的Canvas组件功能是如此的强大,不怪很多牛人预言Flash已死,死不死 不是我要关心的,我关心的C ...

  4. html5 图片文字提取,HTML5 Canvas:获取canvas内容-toDataURL()

    我们可以通过canvas的toDataURL()方法来获取绘制在HTML5 canvas中的内容.做法类似下面的示例代码: var canvas = document.getElementById(& ...

  5. html5图片本地缓存,HTML5: 本地缓存

    HTML5 提供了两种在client存储数据的新对象: localStorage:没有时间限制的数据存储,在同一个浏览器中,仅仅要没被手动清理,第二天.第二周或下一年之后,数据依旧可用. sessio ...

  6. html5图片剪切板,Html5剪切板功能的实现方法

    Html5剪切板功能的实现方法 发布时间:2020-10-23 16:45:29 来源:亿速云 阅读:68 作者:小新 这篇文章主要介绍了Html5剪切板功能的实现方法,具有一定借鉴价值,需要的朋友可 ...

  7. 帝国html5图片站模板,html5响应式自适应帝国CMS整站模板源码瀑布流文章图片资讯文章站...

    版本更新 模板已经适配到最新帝国CMS7.5(部分早期源码模板没有适配到最新)!截止2019-持续追踪更新针对机房黑产利用,和帝国CMS一个XS漏洞利用方法的堵截!正式版已补,请已运营的小伙伴找我查看 ...

  8. html5图片看不见,html5 绘制图片 drawImage

    要在绘图上下文中绘制图片,可以使用 drawImage 方法.该方法有三种不同 的参数:  drawImage(image,dx,dy)  drawImage(image,dx,dy,dw,dh) ...

  9. html5图片上写字,Html5 canvas画图教程20:在canvas里写字

    文章写到现在才发现我忘了介绍在canvas上写字的方法,呃,这篇补上. 其实在canvas里写字是很简单的,他有两个原生方法,即strokeText(描边文字)和fillText(填充文字)--一看就 ...

  10. html5 图片局部马赛克,html5 canvas 图片打马赛克 demo

    Canvas Mosaic #pic{ display:none; } The size of the original pic need to be 360 pixs. canvas 标签 var ...

最新文章

  1. LeetCode简单题之数组中两元素的最大乘积
  2. Android应用开发相关下载资源
  3. 为什么TCP连接至少3次握手
  4. poj 3233 Matrix Power Series
  5. Sr Software Engineer - Big Data Team
  6. spring api 中文_【每日3分钟技术干货 | 面试题+答案 | Springamp;SpringMVC篇
  7. macos mysql8_macOS + MySql8 问题
  8. redis简单运用,数据类型,适合入门
  9. PO模式-unittest
  10. 华为手机上的网上邻居怎么用_只要华为手机用上鸿蒙OS2.0,刚买的手机我也马上换!...
  11. Word VBA:批量给Word文件添加水印
  12. 几种常用网络传输协议
  13. React通用解决方案——浮层容器
  14. 副号显示无服务器,小升初||网报遇BUG,最全解决方案都在这里了
  15. C语言系列之初识C语言(二)2021.10.19
  16. Linux编程基础案例:第4章Shell编程
  17. “潮经济”的品牌营销和消费模式具有哪些特点?
  18. VS2022配置FreeImage - Windows
  19. Java基础之面向对象详解
  20. Vue——10 - webpack打包保姆级教程01——打包js、json、css、less、html、背景图片以及图片、字体(Font)文件,devsever,生产环境配置以及css的兼容写法

热门文章

  1. 怎么将计算机的触摸鼠标锁定,这4种方法可以轻松关闭笔记本锁定触控板
  2. springboot整合elasticsearch5.x以及IK分词器做全文检索
  3. 【Unity】实现立体的UI
  4. php转换大小写函数,149-PHP大小写转换函数
  5. Mysql工作原理——redo日志文件和恢复操作
  6. python中np是什么意思_python中np的作用是什么
  7. Android神器:Xposed框架
  8. linux shell自动登录,Shell自动登录并执行命令
  9. linux rescue u盘,linux 0-rescue
  10. java项目笔记 - 第16章:坦克大战1.0