From 81c2b70d0c87d64ab94375341f69c029a50ea265 Mon Sep 17 00:00:00 2001 From: harriet <1317499207@qq.com> Date: Mon, 3 Feb 2020 21:34:20 +0800 Subject: [PATCH] first commit --- .babelrc | 21 + .npmignore | 8 + README.md | 41 + dist/flip.min.js | 2461 +++++++++++++++++++++++++++++++++++ dist/flip.min.js.map | 1 + package.json | 51 + src/App.vue | 227 ++++ src/index.js | 2 + src/turn_controller.js | 708 ++++++++++ src/turn_page.vue | 325 +++++ src/turn_page_controller.js | 36 + webpack.config.js | 54 + 12 files changed, 3935 insertions(+) create mode 100644 .babelrc create mode 100644 .npmignore create mode 100644 README.md create mode 100644 dist/flip.min.js create mode 100644 dist/flip.min.js.map create mode 100644 package.json create mode 100644 src/App.vue create mode 100644 src/index.js create mode 100644 src/turn_controller.js create mode 100644 src/turn_page.vue create mode 100644 src/turn_page_controller.js create mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..15f1ea8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,21 @@ +{ + "presets": [ + [ + "env", + { + "modules": false, + "targets": { + "browsers": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] + } + } + ], + "stage-2" + ], + "plugins": [ + "transform-runtime" + ] +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e8022b6 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +.* +*.md +*.yml +build/ +node_modules/ +src/ +test/ +gulpfile.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..024c0ca --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# vue-flip-page +vue翻页组件 + +方法 +change (改变页面) +tap  (点击) +turning (正在翻页) +prev (前一页) +next (后一页) +翻到指定页面: + +handleSwitchManual(index) { + if (index === this.currentIndex) return; + this.$refs["turn"].toPage(index); + this.currentIndex = index; + this.goods_id = this.manuals[this.currentIndex].goods_id; + this.show = false; + }, + +传入参数: + +| 参数 | type | example | describe | +| ------ | ---- | -------- | ---------- | +| width | number | 375 | 宽度 | +| height | number | 667 | 高度 | +| data | Array | [ { "picture_image": "https://dev8.yunzmall.com/static/upload/image/8a2b418254cf521ff3e668fa33ac07ee.png", }, { "picture_image": "https://dev8.yunzmall.com/static/upload/image/e7cf7880531ff9cbb91902630c808359.png", }] |传入的数据 | + + +npm包  npm install vue-calendar-l + +在需要用到的页面中(注意 一个页面目前只能引入一次) + +import flipPage from "vue-flip-page"; + +components: { flipPage } + +例子: + + +效果: + diff --git a/dist/flip.min.js b/dist/flip.min.js new file mode 100644 index 0000000..1e4874d --- /dev/null +++ b/dist/flip.min.js @@ -0,0 +1,2461 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else { + var a = factory(); + for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; + } +})(typeof self !== 'undefined' ? self : this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/dist/"; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 15); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +// Thank's IE8 for his funny defineProperty +module.exports = !__webpack_require__(4)(function () { + return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; +}); + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); +if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef + + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +var core = module.exports = { version: '2.6.11' }; +if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +module.exports = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + +module.exports = function (exec) { + try { + return !!exec(); + } catch (e) { + return true; + } +}; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +// css base code, injected by the css-loader +module.exports = function(useSourceMap) { + var list = []; + + // return the list of modules as css string + list.toString = function toString() { + return this.map(function (item) { + var content = cssWithMappingToString(item, useSourceMap); + if(item[2]) { + return "@media " + item[2] + "{" + content + "}"; + } else { + return content; + } + }).join(""); + }; + + // import a list of modules into the list + list.i = function(modules, mediaQuery) { + if(typeof modules === "string") + modules = [[null, modules, ""]]; + var alreadyImportedModules = {}; + for(var i = 0; i < this.length; i++) { + var id = this[i][0]; + if(typeof id === "number") + alreadyImportedModules[id] = true; + } + for(i = 0; i < modules.length; i++) { + var item = modules[i]; + // skip already imported module + // this implementation is not 100% perfect for weird media query combinations + // when a module is imported multiple times with different media queries. + // I hope this will never occur (Hey this way we have smaller bundles) + if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { + if(mediaQuery && !item[2]) { + item[2] = mediaQuery; + } else if(mediaQuery) { + item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; + } + list.push(item); + } + } + }; + return list; +}; + +function cssWithMappingToString(item, useSourceMap) { + var content = item[1] || ''; + var cssMapping = item[3]; + if (!cssMapping) { + return content; + } + + if (useSourceMap && typeof btoa === 'function') { + var sourceMapping = toComment(cssMapping); + var sourceURLs = cssMapping.sources.map(function (source) { + return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' + }); + + return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); + } + + return [content].join('\n'); +} + +// Adapted from convert-source-map (MIT) +function toComment(sourceMap) { + // eslint-disable-next-line no-undef + var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); + var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; + + return '/*# ' + data + ' */'; +} + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra + Modified by Evan You @yyx990803 +*/ + +var hasDocument = typeof document !== 'undefined' + +if (typeof DEBUG !== 'undefined' && DEBUG) { + if (!hasDocument) { + throw new Error( + 'vue-style-loader cannot be used in a non-browser environment. ' + + "Use { target: 'node' } in your Webpack config to indicate a server-rendering environment." + ) } +} + +var listToStyles = __webpack_require__(19) + +/* +type StyleObject = { + id: number; + parts: Array +} + +type StyleObjectPart = { + css: string; + media: string; + sourceMap: ?string +} +*/ + +var stylesInDom = {/* + [id: number]: { + id: number, + refs: number, + parts: Array<(obj?: StyleObjectPart) => void> + } +*/} + +var head = hasDocument && (document.head || document.getElementsByTagName('head')[0]) +var singletonElement = null +var singletonCounter = 0 +var isProduction = false +var noop = function () {} +var options = null +var ssrIdKey = 'data-vue-ssr-id' + +// Force single-tag solution on IE6-9, which has a hard limit on the # of \n\n\n\n// WEBPACK FOOTER //\n// src/App.vue","var hasOwnProperty = {}.hasOwnProperty;\nmodule.exports = function (it, key) {\n return hasOwnProperty.call(it, key);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/_core-js@2.6.11@core-js/library/modules/_has.js\n// module id = 9\n// module chunks = 0","// to indexed object, toObject with fallback for non-array-like ES3 strings\nvar IObject = require('./_iobject');\nvar defined = require('./_defined');\nmodule.exports = function (it) {\n return IObject(defined(it));\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/_core-js@2.6.11@core-js/library/modules/_to-iobject.js\n// module id = 10\n// module chunks = 0","// fallback for non-array-like ES3 and non-enumerable old V8 strings\nvar cof = require('./_cof');\n// eslint-disable-next-line no-prototype-builtins\nmodule.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) {\n return cof(it) == 'String' ? it.split('') : Object(it);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/_core-js@2.6.11@core-js/library/modules/_iobject.js\n// module id = 11\n// module chunks = 0","// 7.2.1 RequireObjectCoercible(argument)\nmodule.exports = function (it) {\n if (it == undefined) throw TypeError(\"Can't call method on \" + it);\n return it;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/_core-js@2.6.11@core-js/library/modules/_defined.js\n// module id = 12\n// module chunks = 0","// 7.1.4 ToInteger\nvar ceil = Math.ceil;\nvar floor = Math.floor;\nmodule.exports = function (it) {\n return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/_core-js@2.6.11@core-js/library/modules/_to-integer.js\n// module id = 13\n// module chunks = 0","\r\n\r\n\r\n\r\n\r\n\n\n\n// WEBPACK FOOTER //\n// src/turn_page.vue","import flipPage from './App.vue'\nexport default flipPage;\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.js","var disposed = false\nfunction injectStyle (ssrContext) {\n if (disposed) return\n require(\"!!vue-loader/node_modules/vue-style-loader!css-loader?sourceMap!../node_modules/_vue-loader@13.7.3@vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-7ba5bd90\\\",\\\"scoped\\\":true,\\\"hasInlineConfig\\\":false}!sass-loader!../node_modules/_vue-loader@13.7.3@vue-loader/lib/selector?type=styles&index=0!./App.vue\")\n}\nvar normalizeComponent = require(\"!../node_modules/_vue-loader@13.7.3@vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../node_modules/_vue-loader@13.7.3@vue-loader/lib/selector?type=script&index=0!./App.vue\"\nimport __vue_script__ from \"!!babel-loader!../node_modules/_vue-loader@13.7.3@vue-loader/lib/selector?type=script&index=0!./App.vue\"\n/* template */\nimport __vue_template__ from \"!!../node_modules/_vue-loader@13.7.3@vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-7ba5bd90\\\",\\\"hasScoped\\\":true,\\\"buble\\\":{\\\"transforms\\\":{}}}!../node_modules/_vue-loader@13.7.3@vue-loader/lib/selector?type=template&index=0!./App.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = \"data-v-7ba5bd90\"\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/App.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-loader/node_modules/vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-7ba5bd90\", Component.options)\n } else {\n hotAPI.reload(\"data-v-7ba5bd90\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/App.vue\n// module id = 16\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7fd9b58 --- /dev/null +++ b/src/index.js @@ -0,0 +1,2 @@ +import flipPage from './App.vue' +export default flipPage; diff --git a/src/turn_controller.js b/src/turn_controller.js new file mode 100644 index 0000000..26e83f6 --- /dev/null +++ b/src/turn_controller.js @@ -0,0 +1,708 @@ +import TurnPage from "./turn_page.vue"; + +const PI = Math.PI; +const A90 = PI / 2; + +function bezier(p1, p2, p3, p4, t) { + const a = 1 - t; + const b = a * a * a; + const c = t * t * t; + return point2D(Math.round(b * p1.x + 3 * t * a * a * p2.x + 3 * t * t * a * p3.x + c * p4.x), Math.round(b * p1.y + 3 * t * a * a * p2.y + 3 * t * t * a * p3.y + c * p4.y)); +} + +function point2D(x, y) { + return { x, y }; +} + +function peelingPoint(a, b, c) { + return { + corner: a, + x: b, + y: c + }; +} + +function _startPoint(corner, width, height, opts) { + opts = opts || 0; + + switch (corner) { + case "tl": + return point2D(opts, opts); + case "tr": + return point2D(width - opts, opts); + case "bl": + return point2D(opts, height - opts); + case "br": + return point2D(width - opts, height - opts); + case "l": + return point2D(opts, 0); + case "r": + return point2D(width - opts, 0); + } +} + +function _endPoint(corner, width, height) { + switch (corner) { + case "tl": + return point2D(width * 2, 0); + case "tr": + return point2D(-width, 0); + case "bl": + return point2D(width * 2, height); + case "br": + return point2D(-width, height); + case "l": + return point2D(width * 2, 0); + case "r": + return point2D(-width, 0); + } +} + +function _translate(x, y) { + return " translate3d(" + x + "px," + y + "px, 0px) "; +} + +function _rotate(degrees) { + return " rotate(" + degrees + "deg) "; +} + +function _scale(a, b) { + return " scale3d(" + a + "," + b + ", 1) "; +} + +function _deg(radians) { + return radians / PI * 180; +} + +function _transform(transform, origin) { + const properties = {}; + + if (origin) properties["transform-origin"] = origin; + + properties["transform"] = transform; + + return properties; +} + +function fold(point, width, height, startPoint) { + let c, d, e, px, gradientSize, h, far, j, k, l, m, n, q; + let w = 0; + let mv = point2D(0, 0); + let df = point2D(0, 0); + let tr = point2D(0, 0); + const B = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2), 2); + const compute = function() { + c = _startPoint(point.corner, width, height); + const k = width - c.x - point.x; + const l = c.y - point.y; + let alpha = Math.atan2(l, k); + let distance = Math.sqrt(k * k + l * l); + const q = point2D(width - c.x - Math.cos(alpha) * width, c.y - Math.sin(alpha) * width); + if (distance > width) { + point.x = q.x; + point.y = q.y; + } + + const rel = point2D(0, 0); + const middle = point2D(0, 0); + + rel.x = c.x ? c.x - point.x : point.x; + rel.y = c.y ? c.y - point.y : point.y; + + middle.x = e ? width - rel.x / 2 : point.x + rel.x / 2; + middle.y = rel.y / 2; + + alpha = A90 - Math.atan2(rel.y, rel.x); + const gamma = alpha - Math.atan2(middle.y, middle.x); + distance = Math.sin(gamma) * Math.sqrt(middle.x * middle.x + middle.y * middle.y); + + tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha)); + + if (alpha > A90) { + tr.x = tr.x + Math.abs(tr.y * rel.y / rel.x); + tr.y = 0; + if (Math.round(tr.x * Math.tan(PI - alpha)) < height) { + point.y = Math.sqrt(Math.pow(height, 2) + 2 * middle.x * rel.x); + if (d) point.y = height - point.y; + return compute(); + } + const D = PI - alpha; + const E = B - height / Math.sin(D); + mv = point2D(Math.round(E * Math.cos(D)), Math.round(E * Math.sin(D))); + if (e) mv.x = -mv.x; + if (d) mv.y = -mv.y; + } + w = Math.round(100 * _deg(alpha)) / 100; + px = Math.round(tr.y / Math.tan(alpha) + tr.x); + const side = width - px; + let G = Math.min(height, side * Math.tan(alpha)); + if (G < 0) G = height; + const sideX = side * Math.cos(2 * alpha); + const sideY = side * Math.sin(2 * alpha); + df = point2D(Math.round(e ? side - sideX : px + sideX), Math.round(d ? sideY : height - sideY)); + const endingPoint = _endPoint(point.corner, width, height); + far = Math.sqrt(Math.pow(endingPoint.x - point.x, 2) + Math.pow(endingPoint.y - point.y, 2)) / width; + gradientSize = Math.min(100, side * Math.sin(alpha)); + h = 1.3 * Math.min(side, G); + tr.x = Math.round(tr.x); + tr.y = Math.round(tr.y); + return true; + }; + const transform = function(tr, c, x, a) { + const l = ["0", "auto"]; + const mvW = (width - B) * x[0] / 100; + const mvH = (height - B) * x[1] / 100; + const cssA = { + left: l[c[0]], + top: l[c[1]], + right: l[c[2]], + bottom: l[c[3]] + }; + const aliasingFk = a !== 90 && a !== -90 ? e ? -1 : 1 : 0; + const origin = x[0] + "% " + x[1] + "%"; + const styles = []; + + styles.push({ ...cssA, ..._transform(_rotate(a) + _translate(tr.x + aliasingFk, tr.y), origin) }); + styles.push({ ...cssA, ..._transform(_rotate(a) + _translate(tr.x + df.x - mv.x - width * x[0] / 100, tr.y + df.y - mv.y - height * x[1] / 100) + _rotate(Math.round(100 * (180 / a - 2) * a) / 100), origin) }); + styles.push(_transform(_translate(-tr.x + mvW - aliasingFk, -tr.y + mvH) + _rotate(-a), origin)); + styles.push(_transform(_translate(width - tr.x + mv.x + mvW, -tr.y + mv.y + mvH) + _rotate(-a), origin)); + + let z, C, D; + if (d) { + if (e) { + C = a - 90; + D = px - 50; + gradientSize = -gradientSize; + z = "50% 25%"; + } else { + C = a - 270; + D = width - px - 50; + z = "50% 25%"; + } + } else { + if (e) { + D = px - 50; + C = a - 270; + gradientSize = -gradientSize; + z = "50% 75%"; + } else { + D = width - px - 50; + C = a - 90; + z = "50% 75%"; + } + } + let E = Math.max(0.5, 2 - far); + if (E > 1) E = E >= 1.7 ? (2 - E) / 0.3 : 1; + styles.push({ opacity: Math.round(100 * E) / 100, ..._transform(_translate(D, 0) + _rotate(C) + _scale(gradientSize / 100, 1), z) }); + if (d) { + if (e) { + C = -270 - a; + h = -h; + D = width - px - 20; + z = "20% 25%"; + } else { + C = -90 - a; + D = px - 20; + z = "20% 25%"; + } + } else { + if (e) { + C = -90 - a; + D = width - px - 20; + h = -h; + z = "20% 75%"; + } else { + C = 90 - a; + D = px - 20; + z = "20% 75%"; + } + } + E = far < 0.3 ? far / 0.3 : 1; + styles.push({ opacity: Math.round(100 * E) / 100, ..._transform(_translate(D, 0) + _rotate(C) + _scale(-h / 100, 1), z) }); + return styles; + }; + + switch (point.corner) { + case "l": + m = point.y - startPoint.y; + n = point.x; + q = Math.atan2(m, n); + if (q > 0) { + j = startPoint.y; + k = Math.sqrt(n * n + m * m); + l = 2 * j * Math.sin(q) + k; + point.x = l * Math.cos(q); + point.y = l * Math.sin(q); + point.corner = "tl"; + e = true; + d = true; + compute(); + return transform(tr, [1, 0, 0, 1], [100, 0], w); + } else { + q = -q; + j = height - startPoint.y; + k = Math.sqrt(n * n + m * m); + l = 2 * j * Math.cos(A90 - q) + k; + point.x = l * Math.cos(q); + point.y = height - l * Math.sin(q); + point.corner = "bl"; + e = true; + compute(); + return transform(point2D(tr.x, -tr.y), [1, 1, 0, 0], [100, 100], -w); + } + case "r": + m = startPoint.y - point.y; + n = width - point.x; + q = Math.atan2(m, n); + if (q < 0) { + j = startPoint.y; + q = -q; + k = Math.sqrt(n * n + m * m); + l = 2 * j * Math.sin(q) + k; + point.x = width - l * Math.cos(q); + point.y = l * Math.sin(q); + point.corner = "tr"; + d = true; + compute(); + return transform(point2D(-tr.x, tr.y), [0, 0, 0, 1], [0, 0], -w); + } else { + j = height - startPoint.y; + k = Math.sqrt(n * n + m * m); + l = 2 * j * Math.cos(A90 - q) + k; + point.x = width - l * Math.cos(q); + point.y = height - l * Math.sin(q); + point.corner = "br"; + compute(); + return transform(point2D(-tr.x, -tr.y), [0, 1, 1, 0], [0, 100], w); + } + case "tl": + d = true; + e = true; + point.x = Math.max(point.x, 1); + c = _startPoint("tl", width, height); + compute(); + return transform(tr, [1, 0, 0, 1], [100, 0], w); + case "tr": + d = true; + point.x = Math.min(point.x, width - 1); + compute(); + return transform(point2D(-tr.x, tr.y), [0, 0, 0, 1], [0, 0], -w); + case "bl": + e = true; + point.x = Math.max(point.x, 1); + compute(); + return transform(point2D(tr.x, -tr.y), [1, 1, 0, 0], [100, 100], -w); + case "br": + point.x = Math.min(point.x, width - 1); + compute(); + return transform(point2D(-tr.x, -tr.y), [0, 1, 1, 0], [0, 100], w); + } +} + +function animatef(point) { + if (!point.to.length) point.to = [point.to]; + if (!point.from.length) point.from = [point.from]; + + const diff = []; + const len = point.to.length; + const time = Date.now(); + + const frame = function() { + const v = []; + const timeDiff = Math.min(point.duration, Date.now() - time); + + for (let i = 0; i < len; i++) v.push(easing(1, timeDiff, point.from[i], diff[i], point.duration)); + + point.frame(len === 1 ? v[0] : v); + + if (timeDiff === point.duration) { + if (point.complete) point.complete(); + } else { + requestAnimationFrameId = requestAnimationFrame(frame); + } + }; + + for (let i = 0; i < len; i++) diff.push(point.to[i] - point.from[i]); + + const easing = function(x, t, b, c, data) { + return c * Math.sqrt(1 - (t = t / data - 1) * t) + b; + }; + + let requestAnimationFrameId; + + frame(); + + return { + stop() { + if (point.complete) point.complete(); + cancelAnimationFrame(requestAnimationFrameId); + } + }; +} + +function turnPage(p1, duration, width, height, frame, complete) { + const p4 = _endPoint(p1.corner, width, height); + return animatef({ + from: 0, + to: 1, + frame: function(v) { + const np = bezier(p1, p1, p4, p4, v); + frame({ x: np.x, y: np.y, corner: p1.corner }); + }, + complete: function() { + complete(); + }, + duration: duration + }); +} + +function hideFoldedPage(p1, width, height, frame, complete) { + const p4 = _startPoint(p1.corner, width, height); + const top = p1.corner.substr(0, 1) === "t"; + const delta = top ? Math.min(0, p1.y - p4.y) / 2 : Math.max(0, p1.y - p4.y) / 2; + const p2 = point2D(p1.x, p1.y + delta); + const p3 = point2D(p4.x, p4.y - delta); + return animatef({ + from: 0, + to: 1, + frame: function(v) { + const np = bezier(p1, p2, p3, p4, v); + frame({ x: np.x, y: np.y, corner: p1.corner }); + }, + complete: function() { + complete(); + }, + duration: 600 + }); +} + +function showFoldedPage(to, width, height, frame, complete) { + const point = _startPoint(to.corner, width, height, 1); + + return animatef({ + from: [point.x, point.y], + to: [to.x, to.y], + duration: 300, + frame: function(v) { + const x = Math.round(v[0]); + const y = Math.round(v[1]); + frame({ x, y, corner: to.corner }); + }, + complete: function() { + complete(); + } + }); +} + +export default { + components: { TurnPage }, + props: { + data: Array, + width: { + type: Number, + default: 0 + }, + height: { + type: Number, + default: 0 + } + }, + data() { + return { + defaultStyles: [{}, {}, {}, {}, {}, {}], + startPoint: null, + relPoint: null, + invalidTouch: false, + action: null, + runAnimation: null, + viewIndex: 0, + turnPage: null, + backPage: 0, + turnActive: false, + showLastCoverPage: false, + touchTimeline: [], + styles: [{}, {}, {}, {}, {}, {}], + isStart: false + }; + }, + mounted() { + }, + activated() { + this.$nextTick(() => { + this.TouchMove(); + }); + }, + deactivated() { + this.$nextTick(() => { + this.TouchMoveOut(); + }); + }, + methods: { + TouchMove() { + if (this.fun.getPhoneEnv() == 3) { + this.$refs.turn.addEventListener("mousemove", this.handleManualTouchMove, false); + this.$refs.turn.addEventListener("mousedown", this.handleManualTouchStart, false); + this.$refs.turn.addEventListener("mouseup", this.handleManualTouchEnd, false); + } else { + this.$refs.turn.addEventListener("touchmove", this.handleManualTouchMove, false); + this.$refs.turn.addEventListener("touchstart", this.handleManualTouchStart, false); + this.$refs.turn.addEventListener("touchend", this.handleManualTouchEnd, false); + } + }, + TouchMoveOut() { + if (this.fun.getPhoneEnv() == 3) { + this.$refs.turn.removeEventListener("mousemove", this.handleManualTouchMove, false); + this.$refs.turn.removeEventListener("mousedown", this.handleManualTouchStart, false); + this.$refs.turn.removeEventListener("mouseup", this.handleManualTouchEnd, false); + } else { + this.$refs.turn.removeEventListener("touchmove", this.handleManualTouchMove, false); + this.$refs.turn.removeEventListener("touchstart", this.handleManualTouchStart, false); + this.$refs.turn.removeEventListener("touchend", this.handleManualTouchEnd, false); + } + }, + handleManualTouchStart(e) { + this.isStart = true; + let x = ""; + let y = ""; + if (this.runAnimation) { + this.runAnimation.stop(); + this.updateTurn(); + } + + const { width, height, viewIndex } = this; + if (this.fun.getPhoneEnv() == 3) { + x = e.clientX - (window.innerWidth - width) / 2; + y = e.clientY - (window.innerHeight - height) / 2; + } else { + x = e.touches[0].clientX - (window.innerWidth - width) / 2; + y = e.touches[0].clientY - (window.innerHeight - height) / 2; + } + + this.startPoint = peelingPoint("r", x, y); + this.touchTimeline = [{ t: Date.now(), x }]; + + if (x < width / 2) { + this.action = "backward"; + if (viewIndex <= 0) { + this.invalidTouch = true; + return; + } + this.turnPage = viewIndex - 1; + this.backPage = viewIndex; + } else { + this.action = "forward"; + if (viewIndex >= this.data.length - 1) { + this.invalidTouch = true; + return; + } + this.turnPage = viewIndex; + this.backPage = viewIndex + 1; + } + // console.log(x,this.touchTimeline) + this.readyTurn(); + }, + handleManualTouchMove(e) { + let x = ""; + let y = ""; + if (this.isStart) { + const { width, height } = this; + if (this.fun.getPhoneEnv() == 3) { + x = e.clientX - (window.innerWidth - width) / 2; + y = e.clientY - (window.innerHeight - height) / 2; + } else { + x = e.touches[0].clientX - (window.innerWidth - width) / 2; + y = e.touches[0].clientY - (window.innerHeight - height) / 2; + } + + const corner = this.startPoint.corner; + if (this.action === "forward" && this.startPoint.x < x) return; + if (this.action === "backward" && this.startPoint.x > x) return; + + this.touchTimeline.push({ t: Date.now(), x }); + const point = peelingPoint(corner, x, y); + + if (this.invalidTouch) return; + + this.updateTurn(fold(point, width, height, this.startPoint)); + this.relPoint = point; + this.turnActive = true; + } + + }, + handleManualTouchEnd() { + this.isStart = false; + const action = this.action; + + if (this.touchTimeline.length < 2) { + if (this.touchTimeline.length) { + this.handleManualTaped(); + } else { + this.turnPage = null; + this.backPage = this.viewIndex; + } + + this.invalidTouch = false; + this.touchTimeline = []; + return; + } + + if (this.invalidTouch) { + if (action === "forward" && this.isSwipe(action)) { + // this.showLastCoverPage = true + this.$emit("next", { to: this.viewIndex + 1, from: this.viewIndex }); + } + + if (action === "backward" && this.isSwipe(action)) { + this.$emit("prev", { to: this.viewIndex - 1, from: this.viewIndex }); + } + } else { + if (action === "forward") { + if (this.isSwipe(action)) { + this.toPage(this.viewIndex + 1, this.relPoint); + } else { + this.hideFolded(this.relPoint, () => { + }); + } + } + + if (action === "backward") { + if (this.isSwipe(action)) { + this.toPage(this.viewIndex - 1, this.relPoint); + } else { + this.turn(this.relPoint, () => { + }); + } + } + } + + this.invalidTouch = false; + this.touchTimeline = []; + }, + handleManualTaped() { + const { width } = this; + const x = this.touchTimeline[0].x; + + this.action = null; + if (x < width / 4) return this.toPage(this.viewIndex - 1); + if (x > width / 4 * 3) return this.toPage(this.viewIndex + 1); + + this.$emit("tap"); + }, + readyTurn() { + const { width, height } = this; + const point = this.action === "forward" ? { x: width, y: 0 } : { x: -width, y: 0 }; + const fromPoint = peelingPoint("tr", point.x, point.y); + // console.log(fromPoint,"fromPoint"); + this.updateTurn(fold(fromPoint, width, height)); + }, + updateTurn(style = [{}, {}, {}, {}, {}, {}]) { + this.styles = style; + }, + isSwipe(action) { + const first = this.touchTimeline[0]; + const last = this.touchTimeline[this.touchTimeline.length - 1]; + const swipeTime = Date.now() - first.t; + const minTime = 200; + const minDistance = 20; + + if (action === "forward") return first.x - last.x > 50 || (first.x - last.x > minDistance && swipeTime < minTime); + if (action === "backward") return last.x - first.x > 50 || (last.x - first.x > minDistance && swipeTime < minTime); + return false; + }, + toPage(toPage, startPoint) { + const fromPage = this.viewIndex; + + if (toPage > fromPage) this.$emit("next", { to: toPage, from: fromPage }); + if (toPage < fromPage) this.$emit("prev", { to: toPage, from: fromPage }); + + if (toPage === fromPage || toPage < 0 || toPage > this.data.length - 1) return; + + if (this.runAnimation) { + this.runAnimation.stop(); + } + + const { width, height } = this; + const point = toPage > fromPage ? { x: width, y: height - 45 } : { x: -width, y: 45 }; + const fromPoint = peelingPoint("br", point.x, point.y); + + if (toPage > fromPage) { + if (!this.action) { + this.action = "forward"; + this.turnPage = fromPage; + this.backPage = toPage; + this.turnActive = true; + } + + this.turn(startPoint || fromPoint, () => { + this.viewIndex = toPage; + this.turnPage = null; + this.backPage = toPage; + // console.log('完成翻页,当前页', this.backPage) + this.updateTurn(); + this.turnActive = false; + this.$emit("change", this.backPage); + }); + } else { + if (!this.action) { + this.action = "backward"; + this.turnPage = toPage; + this.backPage = fromPage; + } + + this.hideFolded(startPoint || fromPoint, () => { + this.viewIndex = toPage; + this.turnPage = null; + this.backPage = toPage; + // console.log('完成翻页,当前页', this.backPage) + this.updateTurn(); + this.turnActive = false; + this.$emit("change", this.backPage); + }); + } + // console.log('this.turnActive', this.turnPage, this.turnActive) + this.$emit("turning", this.backPage); + }, + turn(fromPoint, complete) { + const { width, height } = this; + this.runAnimation = turnPage(fromPoint, 600, width, height, point => { + this.updateTurn(fold(point, width, height)); + }, () => { + complete(); + this.action = null; + this.turnPage = null; + this.backPage = this.viewIndex; + this.updateTurn(); + this.runAnimation = null; + }); + }, + hideFolded(fromPoint, complete) { + const { width, height } = this; + this.runAnimation = hideFoldedPage(fromPoint, width, height, point => { + this.updateTurn(fold(point, width, height)); + }, () => { + complete(); + this.action = null; + this.turnPage = null; + this.backPage = this.viewIndex; + this.updateTurn(); + this.turnActive = false; + this.runAnimation = null; + }); + }, + handleCoverTouchStart(e) { + const x = e.touches[0].clientX; + this.touchTimeline = [{ t: Date.now(), x }]; + }, + handleCoverTouchMove(e) { + const x = e.touches[0].clientX; + this.touchTimeline.push({ t: Date.now(), x }); + }, + handleCoverTouchEnd(e) { + const first = this.touchTimeline[0]; + const last = this.touchTimeline[this.touchTimeline.length - 1]; + if (last.x - first.x > 20 && last.t - first.t < 200) { + this.showLastCoverPage = false; + } + } + } +}; diff --git a/src/turn_page.vue b/src/turn_page.vue new file mode 100644 index 0000000..5b05718 --- /dev/null +++ b/src/turn_page.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/src/turn_page_controller.js b/src/turn_page_controller.js new file mode 100644 index 0000000..0c415bb --- /dev/null +++ b/src/turn_page_controller.js @@ -0,0 +1,36 @@ +export default { + props: { + item: Object, + index: Number, + width: { + type: Number, + default: 0 + }, + height: { + type: Number, + default: 0 + }, + length: { + type: Number, + default: 0 + }, + active: { // 翻动效果生效 + type: Boolean, + default: false + }, + styles: { + type: Array, + default: () => [{}, {}, {}, {}, {}, {}] + } + }, + data() { + return {}; + }, + mounted() { + }, + computed: { + clipSize() { + return Math.sqrt(Math.pow(this.width, 2) + Math.pow(this.height, 2), 2); + } + } +}; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..1ffc9e8 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,54 @@ +const path = require("path"); +const webpack = require("webpack"); +const uglify = require("uglifyjs-webpack-plugin"); + +module.exports = { + devtool: 'source-map', + entry: "./src/index.js",//入口文件,就是上步骤的src目录下的index.js文件, + output: { + path: path.resolve(__dirname, './dist'),//输出路径,就是上步骤中新建的dist目录, + publicPath: '/dist/', + filename: 'flip.min.js', + libraryTarget: 'umd', + umdNamedDefine: true + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader' + }, + { + test: /\.scss/, + use: [ + {loader: "style-loader"}, + {loader: "css-loader"}, + {loader: "sass-loader"} + ] + }, + { + test: /\.js$/, + exclude: /node_modules/, + options:{ + presets:["es2015"] + }, + loader: 'babel-loader' + }, + { + test: /\.(png|jpg|gif|ttf|svg|woff|eot)$/, + loader: 'url-loader', + query: { + limit: 30000, + name: '[name].[ext]?[hash]' + } + } + ] + }, + plugins: [ + new webpack.DefinePlugin({ + "process.env": { + NODE_ENV: JSON.stringify("production") + } + }) + ] +};