UNPKG

press-ui

Version:

简单、易用的跨端组件库,兼容 Vue2 和 Vue3,同时支持 uni-app和普通 Vue 项目

207 lines (186 loc) 6.3 kB
/* eslint-disable no-param-reassign */ /** * 用于反解析渐变字符串为对象 * https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex */ // eslint-disable-next-line import/no-unresolved import tinyColor from 'tinycolor2'; /** * Utility combine multiple regular expressions. * * @param {RegExp[]|string[]} regexpList List of regular expressions or strings. * @param {string} flags Normal RegExp flags. */ const combineRegExp = (regexpList, flags) => { let source = ''; for (let i = 0; i < regexpList.length; i++) { if (typeof regexpList[i] === 'string') { source += regexpList[i]; } else { source += (regexpList[i]).source; } } return new RegExp(source, flags); }; /** * Generate the required regular expressions once. * * Regular Expressions are easier to manage this way and can be well described. * * @result {object} Object containing regular expressions. */ const generateRegExp = () => { // Note any variables with "Capture" in name include capturing bracket set(s). const searchFlags = 'gi'; // ignore case for angles, "rgb" etc const rAngle = /(?:[+-]?\d*\.?\d+)(?:deg|grad|rad|turn)/; // Angle +ive, -ive and angle types // optional 2nd part const rSideCornerCapture = /to\s+((?:(?:left|right|top|bottom)(?:\s+(?:top|bottom|left|right))?))/; const rComma = /\s*,\s*/; // Allow space around comma. const rColorHex = /#(?:[a-f0-9]{6}|[a-f0-9]{3})/; // 3 or 6 character form const rDigits3 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)/; const // "(1, 2, 3)" rDigits4 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*,\s*\d*\.?\d+\)/; const // "(1, 2, 3, 4)" rValue = /(?:[+-]?\d*\.?\d+)(?:%|[a-z]+)?/; const // ".9", "-5px", "100%". rKeyword = /[_a-z-][_a-z0-9-]*/; const // "red", "transparent". rColor = combineRegExp( ['(?:', rColorHex, '|', '(?:rgb|hsl)', rDigits3, '|', '(?:rgba|hsla)', rDigits4, '|', rKeyword, ')'], '', ); const rColorStop = combineRegExp([rColor, '(?:\\s+', rValue, '(?:\\s+', rValue, ')?)?'], ''); const // Single Color Stop, optional %, optional length. rColorStopList = combineRegExp(['(?:', rColorStop, rComma, ')*', rColorStop], ''); const // List of color stops min 1. rLineCapture = combineRegExp(['(?:(', rAngle, ')|', rSideCornerCapture, ')'], ''); const // Angle or SideCorner rGradientSearch = combineRegExp(['(?:(', rLineCapture, ')', rComma, ')?(', rColorStopList, ')'], searchFlags); const // Capture 1:"line", 2:"angle" (optional), 3:"side corner" (optional) and 4:"stop list". rColorStopSearch = combineRegExp( ['\\s*(', rColor, ')', '(?:\\s+', '(', rValue, '))?', '(?:', rComma, '\\s*)?'], searchFlags, ); // Capture 1:"color" and 2:"position" (optional). return { gradientSearch: rGradientSearch, colorStopSearch: rColorStopSearch, }; }; /** * Actually parse the input gradient parameters string into an object for reusability. * * * @note Really this only supports the standard syntax not historical versions, see MDN for details * https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient * * @param regExpLib * @param {string} input * @returns {object|undefined} */ const parseGradient = (regExpLib, input) => { let result; let matchColorStop; let stopResult; // reset search position, because we reuse regex. regExpLib.gradientSearch.lastIndex = 0; const matchGradient = regExpLib.gradientSearch.exec(input); if (matchGradient) { result = { original: matchGradient[0], colorStopList: [], }; // Line (Angle or Side-Corner). if (matchGradient[1]) { // eslint-disable-next-line prefer-destructuring result.line = matchGradient[1]; } // Angle or undefined if side-corner. if (matchGradient[2]) { // eslint-disable-next-line prefer-destructuring result.angle = matchGradient[2]; } // Side-corner or undefined if angle. if (matchGradient[3]) { // eslint-disable-next-line prefer-destructuring result.sideCorner = matchGradient[3]; } // reset search position, because we reuse regex. regExpLib.colorStopSearch.lastIndex = 0; // Loop though all the color-stops. matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]); while (matchColorStop) { stopResult = { color: matchColorStop[1], }; // Position (optional). if (matchColorStop[2]) { // eslint-disable-next-line prefer-destructuring stopResult.position = matchColorStop[2]; } result.colorStopList.push(stopResult); // Continue searching from previous position. matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]); } } // Can be undefined if match not found. return result; }; const REGEXP_LIB = generateRegExp(); const REG_GRADIENT = /.*gradient\s*\(((?:\([^)]*\)|[^)(]*)*)\)/gim; /** * 验证是否是渐变字符串 * @param input * @returns */ export const isGradientColor = (input) => { REG_GRADIENT.lastIndex = 0; return REG_GRADIENT.exec(input); }; // 边界字符串和角度关系 const sideCornerDegreeMap = { top: 0, right: 90, bottom: 180, left: 270, 'top left': 225, 'left top': 225, 'top right': 135, 'right top': 135, 'bottom left': 315, 'left bottom': 315, 'bottom right': 45, 'right bottom': 45, }; /** * 解析渐变字符串为 GradientColors 对象 * @param input * @returns */ export const parseGradientString = (input) => { const match = isGradientColor(input); if (!match) { return false; } const gradientColors = { points: [], degree: 0, }; const result = parseGradient(REGEXP_LIB, match[1]); if (result.original.trim() !== match[1].trim()) { return false; } const points = result.colorStopList.map(({ color, position }) => { const point = Object.create(null); point.color = tinyColor(color).toRgbString(); point.left = parseFloat(position); return point; }); gradientColors.points = points; let degree = parseInt(result.angle, 10); if (Number.isNaN(degree)) { degree = sideCornerDegreeMap[result.sideCorner] || 90; } gradientColors.degree = degree; return gradientColors; }; export default parseGradientString;