drawer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. layui.define(['jquery', 'element', 'layer', 'loading'], function (exports) {
  2. "use strict";
  3. /**
  4. * Drawer component
  5. * */
  6. var MOD_NAME = 'drawer',
  7. $ = layui.jquery,
  8. element = layui.element,
  9. layer = layui.layer,
  10. loading = layui.loading;
  11. var drawer = new function () {
  12. /**
  13. * open drawer
  14. * */
  15. this.open = function (option) {
  16. // 默认使用 legacy 模式
  17. if (option.legacy === undefined) {
  18. option.legacy = true;
  19. };
  20. if (option.legacy) {
  21. var obj = new mSlider({
  22. target: option.target,
  23. dom: option.dom,
  24. direction: option.direction,
  25. distance: option.distance,
  26. time: option.time ? option.time : 0,
  27. maskClose: option.maskClose,
  28. callback: option.success
  29. });
  30. obj.open();
  31. return obj;
  32. } else {
  33. return layerDrawer(option);
  34. }
  35. }
  36. this.title = layer.title;
  37. this.style = layer.style;
  38. this.close = layer.close;
  39. this.closeAll = layer.closeAll;
  40. }
  41. /**
  42. *
  43. * 封装 layer.open
  44. * type,anim,move,fixed不可用,其它参数和 layer.open 一致
  45. * @param {LayerOption} option
  46. * @returns 原生 layer 的 index
  47. */
  48. function layerDrawer(option) {
  49. var opt = normalizeOption(option)
  50. if (opt.target) appendToTarget(opt);
  51. if (opt.url) loadFragment(opt);
  52. var layerIndex = layer.open(opt);
  53. return layerIndex;
  54. }
  55. /**
  56. * 加载 HTML 片段到 layer content
  57. * @param {*} option
  58. */
  59. function loadFragment(option) {
  60. option.success = Aspect(option.success, function (layero, index) {
  61. var layerID = "#" + layero.attr("id");
  62. loading.block({
  63. type: 1,
  64. elem: layerID,
  65. msg: ''
  66. });
  67. $.ajax({
  68. url: option.url,
  69. dataType: "html",
  70. success: function (result) {
  71. layero.children('.layui-layer-content').html(result);
  72. loading.blockRemove(layerID);
  73. }
  74. })
  75. })
  76. }
  77. /**
  78. *将 layer 挂载到指定节点
  79. * @param {object} opt
  80. */
  81. function appendToTarget(opt) {
  82. var targetDOM = $(opt.target);
  83. var contentDOM = $(opt.content);
  84. contentDOM.appendTo(targetDOM);
  85. opt.skin = getDrawerAnimationClass(opt.offset, true);
  86. opt.offset = calcOffset(opt.offset, opt.area, targetDOM);
  87. // 处理关闭后偶现 DOM 仍显示的问题,layer 的 BUG
  88. opt.end = Aspect(opt.end, function () {
  89. contentDOM.css("display", "none");
  90. })
  91. if (opt.shade) {
  92. opt.success = Aspect(opt.success, function (layero, index) {
  93. var shadeDOM = $("#layui-layer-shade" + index);
  94. shadeDOM.css("position", "absolute");
  95. shadeDOM.appendTo(layero.parent());
  96. })
  97. }
  98. }
  99. /**
  100. * 规格化 layer.open 选项,兼容原版 Drawer 所有选项
  101. * @param {LayerOption} option layer.open 的选项
  102. * @returns 规格化后的 layer.open 选项
  103. */
  104. function normalizeOption(option) {
  105. if (option.direction && !option.offset) {
  106. if (option.direction === "right") {
  107. option.offset = "r";
  108. } else if (option.direction === "left") {
  109. option.offset = "l";
  110. } else if (option.direction === "top") {
  111. option.offset = "t";
  112. } else if (option.direction === "bottom") {
  113. option.offset = "b";
  114. } else {
  115. option.offset = "r";
  116. }
  117. }
  118. if (option.distance && !option.area) {
  119. option.area = option.distance;
  120. }
  121. if (option.dom && !option.content) {
  122. option.content = $(option.dom);
  123. }
  124. if (option.maskClose && option.shadeClose === undefined) {
  125. option.shadeClose = (option.maskClose + "").toString() !== "false" ? true : false;
  126. }
  127. option.type = 1
  128. option.anim = -1;
  129. option.move = false;
  130. option.fixed = true;
  131. if (option.iframe) {
  132. option.type = 2;
  133. option.content = option.iframe;
  134. }
  135. if (option.offset === undefined) option.offset = "r";
  136. option.area = calcDrawerArea(option.offset, option.area);
  137. if (option.title === undefined) option.title = false;
  138. if (option.closeBtn === undefined) option.closeBtn = false;
  139. if (option.shade === undefined) option.shade = 0.3;
  140. if (option.shadeClose === undefined) option.shadeClose = true;
  141. if (option.skin === undefined) option.skin = getDrawerAnimationClass(option.offset);
  142. if (option.resize === undefined) option.resize = false;
  143. if (option.success === undefined) option.success = function () { }; // 处理遮罩需要
  144. if (option.end === undefined) option.end = function () { };
  145. return option;
  146. }
  147. /**
  148. * 计算抽屉宽高
  149. * @param {string} offset 抽屉方向 l = 左, r = 右, t = 上, b = 下
  150. * @param {string[] | string} drawerArea 抽屉大小,字符串数组格式:[width, height],字符串格式:百分比或单位 px。
  151. * @returns 抽屉宽高数组
  152. */
  153. function calcDrawerArea(offset, drawerArea) {
  154. if (drawerArea instanceof Array) {
  155. return drawerArea;
  156. }
  157. if (drawerArea === undefined || drawerArea === "auto") {
  158. drawerArea = "30%";
  159. }
  160. if (offset === "l" || offset === "r") {
  161. return [drawerArea, "100%"];
  162. } else if (offset === "t" || offset === "b") {
  163. return ["100%", drawerArea];
  164. }
  165. return [drawerArea, "100%"];
  166. }
  167. /**
  168. * 获取抽屉动画类
  169. * @param {string} offset 抽屉方向
  170. * @param {boolean} 是否 absolute 布局
  171. * @returns 抽屉入场动画类
  172. */
  173. function getDrawerAnimationClass(offset, isAbsolute) {
  174. var positionAbsoluteClass = "position-absolute ";
  175. var prefixClass = "pear-drawer pear-drawer-anim layui-anim layer-anim-";
  176. var suffix = "rl";
  177. if (isAbsolute) {
  178. prefixClass = positionAbsoluteClass + prefixClass;
  179. }
  180. if (offset === "l") {
  181. suffix = "lr";
  182. } else if (offset === "r") {
  183. suffix = "rl";
  184. } else if (offset === "t") {
  185. suffix = "tb";
  186. } else if (offset === "b") {
  187. suffix = "bt";
  188. }
  189. return prefixClass + suffix;
  190. }
  191. /**
  192. * 指定挂载容器重新计算 offset
  193. * @param {*} offset 位置
  194. * @param {*} area 范围大小
  195. * @param {*} targetEl 挂载节点
  196. * @returns 包含抽屉位置信息的数组,[top,left]
  197. */
  198. function calcOffset(offset, area, targetEl) {
  199. if (offset === undefined || offset === "l" || offset === "t") {
  200. offset = "lt";
  201. } else if (offset === "r") {
  202. var left;
  203. if (area instanceof Array) {
  204. area = area[0];
  205. }
  206. if (area.indexOf("%") != -1) {
  207. left = targetEl.innerWidth() * (1 - area.replace("%", "") / 100);
  208. } else {
  209. left = targetEl.innerWidth() - area;
  210. }
  211. offset = [0, left];
  212. } else if (offset === "b") {
  213. var top;
  214. if (area instanceof Array) {
  215. area = area[1];
  216. }
  217. if (area.indexOf("%") != -1) {
  218. top = targetEl.innerHeight() * (1 - area.replace("%", "") / 100);
  219. } else {
  220. top = targetEl.innerHeight() - area;
  221. }
  222. offset = [top, 0];
  223. }
  224. return offset;
  225. }
  226. /**
  227. * 简易的切面
  228. * @param {Function} func 被通知的对象,原函数
  229. * @param {Function | undefined} before 前置通知
  230. * @param {Function | undefined} after 后置通知
  231. * @returns 代理函数
  232. */
  233. function Aspect(target, before, after) {
  234. function proxyFunc() {
  235. if (before && typeof before === "function") {
  236. before.apply(this, arguments)
  237. }
  238. target.apply(this, arguments);
  239. if (after && typeof after === "function") {
  240. after.apply(this, arguments)
  241. }
  242. }
  243. return proxyFunc;
  244. }
  245. exports(MOD_NAME, drawer);
  246. });
  247. /**
  248. * 源码
  249. * */
  250. (function (b, c) {
  251. function a(d) {
  252. this.opts = {
  253. "target": d.target || "body",
  254. "direction": d.direction || "left",
  255. "distance": d.distance || "60%",
  256. "dom": this.Q(d.dom),
  257. "time": d.time || "",
  258. "maskClose": (d.maskClose + "").toString() !== "false" ? true : false,
  259. "callback": d.callback || ""
  260. };
  261. this.rnd = this.rnd();
  262. this.target = this.opts.target;
  263. this.dom = this.opts.dom[0];
  264. this.wrap = "";
  265. this.inner = "";
  266. this.mask = "";
  267. this.init()
  268. }
  269. a.prototype = {
  270. Q: function (d) {
  271. return document.querySelectorAll(d)
  272. },
  273. isMobile: function () {
  274. return navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i) ? true : false
  275. },
  276. addEvent: function (f, e, d) {
  277. if (f.attachEvent) {
  278. f.attachEvent("on" + e, d)
  279. } else {
  280. f.addEventListener(e, d, false)
  281. }
  282. },
  283. rnd: function () {
  284. return Math.random().toString(36).substr(2, 6)
  285. },
  286. init: function () {
  287. var g = this;
  288. if (!g.dom) {
  289. console.log("未正确绑定弹窗容器");
  290. return
  291. }
  292. g.dom.style.display = "block"; // 兼容 layer 捕获层
  293. var d = document.createElement("div");
  294. var e = document.createElement("div");
  295. var f = document.createElement("div");
  296. d.setAttribute("class", "mSlider-main ms-" + g.rnd);
  297. e.setAttribute("class", "mSlider-inner");
  298. f.setAttribute("class", "mSlider-mask");
  299. g.Q(g.target)[0].appendChild(d);
  300. g.Q(".ms-" + g.rnd)[0].appendChild(e);
  301. g.Q(".ms-" + g.rnd)[0].appendChild(f);
  302. g.wrap = g.Q(".ms-" + g.rnd)[0];
  303. g.inner = g.Q(".ms-" + g.rnd + " .mSlider-inner")[0];
  304. g.mask = g.Q(".ms-" + g.rnd + " .mSlider-mask")[0];
  305. g.inner.appendChild(g.dom);
  306. switch (g.opts.direction) {
  307. case "top":
  308. g.top = "0";
  309. g.left = "0";
  310. g.width = "100%";
  311. g.height = g.opts.distance;
  312. g.translate = "0,-100%,0";
  313. break;
  314. case "bottom":
  315. g.bottom = "0";
  316. g.left = "0";
  317. g.width = "100%";
  318. g.height = g.opts.distance;
  319. g.translate = "0,100%,0";
  320. break;
  321. case "right":
  322. g.top = "0";
  323. g.right = "0";
  324. g.width = g.opts.distance;
  325. g.height = document.documentElement.clientHeight + "px";
  326. g.translate = "100%,0,0";
  327. break;
  328. default:
  329. g.top = "0";
  330. g.left = "0";
  331. g.width = g.opts.distance;
  332. g.height = document.documentElement.clientHeight + "px";
  333. g.translate = "-100%,0,0"
  334. }
  335. g.wrap.style.display = "none";
  336. g.wrap.style.position = (g.target === "body" ? "fixed" : "absolute");
  337. g.wrap.style.top = "0";
  338. g.wrap.style.left = "0";
  339. g.wrap.style.width = "100%";
  340. g.wrap.style.height = "100%";
  341. g.wrap.style.zIndex = 9999999;
  342. g.inner.style.position = "absolute";
  343. g.inner.style.top = g.top;
  344. g.inner.style.bottom = g.bottom;
  345. g.inner.style.left = g.left;
  346. g.inner.style.right = g.right;
  347. g.inner.style.width = g.width;
  348. g.inner.style.height = (g.target === "body" ? g.height : "100%");
  349. g.inner.style.backgroundColor = "#fff";
  350. g.inner.style.transform = "translate3d(" + g.translate + ")";
  351. g.inner.style.webkitTransition = "all .2s ease-out";
  352. g.inner.style.transition = "all .2s ease-out";
  353. g.inner.style.zIndex = 10000000;
  354. g.mask.style.width = "100%";
  355. g.mask.style.height = "100%";
  356. g.mask.style.opacity = "0.1";
  357. g.mask.style.backgroundColor = "black";
  358. g.mask.style.zIndex = "9999998";
  359. g.mask.style.webkitBackfaceVisibility = "hidden";
  360. g.events()
  361. },
  362. open: function () {
  363. var d = this;
  364. d.wrap.style.display = "block";
  365. setTimeout(function () {
  366. d.inner.style.transform = "translate3d(0,0,0)";
  367. d.inner.style.webkitTransform = "translate3d(0,0,0)";
  368. d.mask.style.opacity = 0.1
  369. }, 30);
  370. if (d.opts.time) {
  371. d.timer = setTimeout(function () {
  372. d.close()
  373. }, d.opts.time)
  374. }
  375. },
  376. close: function () {
  377. var d = this;
  378. d.timer && clearTimeout(d.timer);
  379. d.inner.style.webkitTransform = "translate3d(" + d.translate + ")";
  380. d.inner.style.transform = "translate3d(" + d.translate + ")";
  381. d.mask.style.opacity = 0;
  382. setTimeout(function () {
  383. d.wrap.style.display = "none";
  384. d.timer = null;
  385. d.opts.callback && d.opts.callback()
  386. }, 300)
  387. },
  388. events: function () {
  389. var d = this;
  390. d.addEvent(d.mask, "touchmove", function (f) {
  391. f.preventDefault()
  392. });
  393. d.addEvent(d.mask, (d.isMobile() ? "touchend" : "click"), function (f) {
  394. if (d.opts.maskClose) {
  395. d.close()
  396. }
  397. })
  398. }
  399. };
  400. b.mSlider = a
  401. })(window);