html5图片弹性布局,HTML5 使用弹性框布局实现可选择和压缩的网格
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 = `
`;
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 = $('
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 使用弹性框布局实现可选择和压缩的网格相关推荐
- 3d布局图 html5,8个实用炫酷的HTML5图片动画应用 | HTML5资源教程
近期我们发布了不少关于 1.CSS3/jQuery实现移动端滑动图片层叠效果 这是一款基于jQuery和CSS3的图片层叠插件,这和我们之前介绍的CSS3图片层叠展开特效有点不同,它支持鼠标滑动切换图 ...
- html5图片区域剪切,HTML5 本地裁剪图片并上传至服务器(老梗)
很多情况下用户上传的图片都需要经过裁剪,比如头像啊什么的.但以前实现这类需求都很复杂,往往需要先把图片上传到服务器,然后返回给用户,让用户确定裁剪坐标,发送给服务器,服务器裁剪完再返回给用户,来回需要 ...
- html5图片灰度显示,HTML5 组件Canvas实现图像灰度化
HTML5发布已经有很长一段时间了,一直以来从来没有仔细的看过,过年刚来随便看看 发现HTML5中的Canvas组件功能是如此的强大,不怪很多牛人预言Flash已死,死不死 不是我要关心的,我关心的C ...
- html5 图片文字提取,HTML5 Canvas:获取canvas内容-toDataURL()
我们可以通过canvas的toDataURL()方法来获取绘制在HTML5 canvas中的内容.做法类似下面的示例代码: var canvas = document.getElementById(& ...
- html5图片本地缓存,HTML5: 本地缓存
HTML5 提供了两种在client存储数据的新对象: localStorage:没有时间限制的数据存储,在同一个浏览器中,仅仅要没被手动清理,第二天.第二周或下一年之后,数据依旧可用. sessio ...
- html5图片剪切板,Html5剪切板功能的实现方法
Html5剪切板功能的实现方法 发布时间:2020-10-23 16:45:29 来源:亿速云 阅读:68 作者:小新 这篇文章主要介绍了Html5剪切板功能的实现方法,具有一定借鉴价值,需要的朋友可 ...
- 帝国html5图片站模板,html5响应式自适应帝国CMS整站模板源码瀑布流文章图片资讯文章站...
版本更新 模板已经适配到最新帝国CMS7.5(部分早期源码模板没有适配到最新)!截止2019-持续追踪更新针对机房黑产利用,和帝国CMS一个XS漏洞利用方法的堵截!正式版已补,请已运营的小伙伴找我查看 ...
- html5图片看不见,html5 绘制图片 drawImage
要在绘图上下文中绘制图片,可以使用 drawImage 方法.该方法有三种不同 的参数: drawImage(image,dx,dy) drawImage(image,dx,dy,dw,dh) ...
- html5图片上写字,Html5 canvas画图教程20:在canvas里写字
文章写到现在才发现我忘了介绍在canvas上写字的方法,呃,这篇补上. 其实在canvas里写字是很简单的,他有两个原生方法,即strokeText(描边文字)和fillText(填充文字)--一看就 ...
- html5 图片局部马赛克,html5 canvas 图片打马赛克 demo
Canvas Mosaic #pic{ display:none; } The size of the original pic need to be 360 pixs. canvas 标签 var ...
最新文章
- LeetCode简单题之数组中两元素的最大乘积
- Android应用开发相关下载资源
- 为什么TCP连接至少3次握手
- poj 3233 Matrix Power Series
- Sr Software Engineer - Big Data Team
- spring api 中文_【每日3分钟技术干货 | 面试题+答案 | Springamp;SpringMVC篇
- macos mysql8_macOS + MySql8 问题
- redis简单运用,数据类型,适合入门
- PO模式-unittest
- 华为手机上的网上邻居怎么用_只要华为手机用上鸿蒙OS2.0,刚买的手机我也马上换!...
- Word VBA:批量给Word文件添加水印
- 几种常用网络传输协议
- React通用解决方案——浮层容器
- 副号显示无服务器,小升初||网报遇BUG,最全解决方案都在这里了
- C语言系列之初识C语言(二)2021.10.19
- Linux编程基础案例:第4章Shell编程
- “潮经济”的品牌营销和消费模式具有哪些特点?
- VS2022配置FreeImage - Windows
- Java基础之面向对象详解
- Vue——10 - webpack打包保姆级教程01——打包js、json、css、less、html、背景图片以及图片、字体(Font)文件,devsever,生产环境配置以及css的兼容写法
热门文章
- 怎么将计算机的触摸鼠标锁定,这4种方法可以轻松关闭笔记本锁定触控板
- springboot整合elasticsearch5.x以及IK分词器做全文检索
- 【Unity】实现立体的UI
- php转换大小写函数,149-PHP大小写转换函数
- Mysql工作原理——redo日志文件和恢复操作
- python中np是什么意思_python中np的作用是什么
- Android神器:Xposed框架
- linux shell自动登录,Shell自动登录并执行命令
- linux rescue u盘,linux 0-rescue
- java项目笔记 - 第16章:坦克大战1.0