这是我的代码:
var Ribbon = /** @class */ (function () {
function Ribbon(svg) {
this.rootPos = Math.random() * 100;
this.states = {
start: {
type: 'start',
start: { thickness: 0, top: 60, left: 0 },
end: { thickness: 0, top: 60, left: 100 },
wave: { amplitude: 0, frequency: 0.008, transitionSpeed: 0.5 }
},
home: {
type: 'home',
start: { thickness: 30, top: 50, left: 0 },
end: { thickness: 15, top: 60, left: 100 },
wave: { amplitude: 40, frequency: 0.015, transitionSpeed: -0.5 }
},
selected: {
type: 'selected',
start: { thickness: 15, top: 80, left: 0 },
end: { thickness: 24, top: 25, left: 100 },
wave: { amplitude: 50, frequency: 0.004, transitionSpeed: 0.5 }
}
};
this.size = { svg: { width: window.innerWidth, height: window.innerHeight } };
this.currentState = JSON.parse(JSON.stringify(this.states.start));
this.rows = 3;
this.lines = [];
this.blocks = [];
this.defaultRestSpeed = 0.009;
this.speed = {
changeState: 2,
rest: 1
};
this.waveLineAttr = {
fill: 'none',
stroke: 'white',
strokeWidth: 1
};
this.svg = svg;
}
Ribbon.prototype.init = function () {
var _this = this;
this.blocks = [
{
type: 'menu',
id: '1',
title: 'Products',
position: { current: 105, target: 5, home: 5 },
width: 20,
row: 2
},
{
type: 'menu',
id: '2',
title: 'Blog',
position: { current: 137, target: 37, home: 37 },
width: 10,
row: 3
},
{
type: 'menu',
title: 'About Us',
id: '3',
position: { current: 147, target: 47, home: 47 },
width: 20,
row: 1
},
{
type: 'menu',
id: '4',
title: 'Contact',
position: { current: 175, target: 75, home: 75 },
width: 15,
row: 2
},
{
type: 'back',
id: 'back',
title: '<-- Back',
position: { current: -100, target: -100, home: -100 },
width: 10,
row: 1
}
];
this.ribbon = this.svg.polygon().attr({
fill: 'red',
stroke: 'none'
});
// this.topWaveGuideLine = this.svg.path().attr(this.waveLineAttr);
// this.bottomWaveGuideLine = this.svg.path().attr(this.waveLineAttr);
this.resizeSVG();
this.tick(0);
this.updateRibbon();
setTimeout(function () {
_this.setState(_this.states.home);
}, 1000);
};
Ribbon.prototype.updateRibbon = function () {
var _this = this;
var freq = this.currentState.wave.frequency;
var pos = this.rootPos;
var amp = this.currentState.wave.amplitude;
var x1 = (this.size.svg.width / 100) * this.currentState.start.left;
var y1 = ((this.size.svg.height / 100) * this.currentState.start.top);
var x2 = (this.size.svg.width / 100) * this.currentState.end.left;
var y2 = (this.size.svg.height / 100) * this.currentState.end.top;
var t1 = ((this.size.svg.height / 100) * this.currentState.start.thickness) / 2;
var t2 = ((this.size.svg.height / 100) * this.currentState.end.thickness) / 2;
this.guidePath = this.getGuidePath(freq, pos, amp, x1, y1, x2, y2);
var topPathPoints = this.getPathOffset(this.guidePath, -t1, -t2);
this.paths = [topPathPoints];
if (this.rows > 1) {
var startHeightSpace = ((this.size.svg.height / 100) * this.currentState.start.thickness);
var endHeightSpace = ((this.size.svg.height / 100) * this.currentState.end.thickness);
for (var i_1 = 1; i_1 < this.rows; i_1++) {
var startOffset = (0 - (startHeightSpace / 2)) + ((startHeightSpace / this.rows) * i_1);
var endOffset = (0 - (endHeightSpace / 2)) + ((endHeightSpace / this.rows) * i_1);
var path = this.getPathOffset(this.guidePath, startOffset, endOffset);
this.paths.push(path);
if (!this.lines[i_1 - 1])
this.lines.push(this.svg.path().attr(this.waveLineAttr));
this.lines[i_1 - 1].attr({ d: this.getWavePathString(path) });
}
}
var bottomPath = this.getPathOffset(this.guidePath, t1, t2);
this.paths.push(bottomPath);
this.ribbon.attr({ points: this.getWavePolygon(topPathPoints, bottomPath.slice(0).reverse()) });
for (var i = 0; i < this.blocks.length; i++) {
var block = this.blocks[i];
var startX = (this.size.svg.width / 100) * block.position.current;
var endX = (this.size.svg.width / 100) * (block.position.current + block.width);
if (startX > this.size.svg.width)
startX = this.size.svg.width;
else if (startX < 0)
startX = 0;
if (endX > this.size.svg.width)
endX = this.size.svg.width;
else if (endX < 0)
endX = 0;
var path1 = this.paths[block.row - 1];
var path2 = this.paths[block.row];
var chunk1 = path1.slice(startX, endX);
var chunk2 = path2.slice(startX, endX);
if (chunk1.length > 1 && chunk2.length > 1) {
var textPaddingX = 20;
var textPaddingY = 10;
var fontSize = this.getBlockFontSize(chunk1, chunk2, textPaddingY);
var boxPolyPoints = this.getWavePolygon(chunk1, chunk2.reverse());
if (!block.svg) {
block.svg = {};
block.svg.group = this.svg.group().attr({ "class": 'block', id: block.id });
block.svg.group.click(function (e) { _this.onBlockClick(e); });
block.svg.box = block.svg.group.polygon().attr({ fill: 'white', stroke: 'none' });
block.svg.mask = this.svg.polygon().attr({ fill: 'white', stroke: 'none' }).toDefs();
block.svg.textpath = block.svg.group.path().attr({ fill: 'none', stroke: 'white' }).toDefs();
block.svg.text = block.svg.group.text(0, 0, block.title).attr({ textpath: block.svg.textpath, mask: block.svg.mask });
}
block.svg.box.attr({ points: boxPolyPoints });
block.svg.mask.attr({ points: boxPolyPoints });
block.svg.text.attr({ fontSize: fontSize });
if (endX - startX > textPaddingX * 2) {
var newPath = this.getPathOffset(path2.slice(startX + textPaddingX, endX - textPaddingX), -textPaddingY, -textPaddingY);
if (newPath.length > 2) {
var newPathString = this.getWavePathString(newPath);
block.svg.textpath.attr({ d: newPathString });
}
}
}
else {
if (block.svg) {
block.svg.group.remove();
block.svg.mask.remove();
block.svg = null;
}
}
}
};
Ribbon.prototype.getGuidePath = function (freq, pos, amp, x1, y1, x2, y2) {
var points = [];
var width = x2 - x1;
var x = x1;
var y = y1;
var yChange = y2 - y1;
while (x++ <= width) {
y = Math.sin(x * freq + pos);
points.push([x, (y1 + ((width - (width - x)) * (yChange / width)) + (y * amp / 2 + amp / 2))]);
}
return points;
};
Ribbon.prototype.getPathOffset = function (points, offsetStart, offsetEnd) {
if (points.length) {
var x1 = points[0][0];
var y1 = points[0][1];
var x2 = points[points.length - 1][0];
var y2 = points[points.length - 1][1];
var yChange = offsetStart - offsetEnd;
var width = x1 - x2;
var newPoints = [];
for (var i = 0; i < points.length; i++) {
var newPoint = [points[i][0], points[i][1]];
newPoint[1] += offsetStart + ((width - (width - i)) * (yChange / width));
newPoints.push(newPoint);
}
return newPoints;
}
return [];
};
Ribbon.prototype.getBlockFontSize = function (topPoints, bottomPoints, padding, maxSize, minSize) {
if (maxSize === void 0) { maxSize = 40; }
if (minSize === void 0) { minSize = 10; }
var startHeight = Math.abs(topPoints[0][1] - bottomPoints[0][1]);
var endHeight = Math.abs(topPoints[topPoints.length - 1][1] - bottomPoints[bottomPoints.length - 1][1]);
var space = startHeight < endHeight ? startHeight : endHeight;
space -= padding * 2;
if (space < minSize)
space = minSize;
if (space > maxSize)
space = maxSize;
return space;
};
Ribbon.prototype.getWavePathString = function (points) {
// if(points.length < 2) return '';
var mapped = _.map(points, function (d) {
return d.join(',');
});
return 'M' + mapped.join(' ');
};
Ribbon.prototype.getWavePolygon = function (topPoints, bottomPoints) {
var allPoints = topPoints.concat(bottomPoints);
var polyString = '';
for (var i = 0; i < allPoints.length; i++) {
polyString += allPoints[i].join(' ') + ' ';
}
return polyString;
};
Ribbon.prototype.onBlockClick = function (e) {
var selectedBlock = null;
for (var i = 0; i < e.path.length; i++) {
if (e.path[i].nodeName == 'g') {
selectedBlock = e.path[i].id;
break;
}
}
if (selectedBlock)
this.selectBlock(selectedBlock);
};
Ribbon.prototype.resizeSVG = function () {
this.svg.attr({
width: this.size.svg.width,
height: this.size.svg.height
});
};
Ribbon.prototype.onResize = function (event) {
this.size.svg.width = event.target.innerWidth;
this.size.svg.height = event.target.innerHeight;
if (this.svg)
this.resizeSVG();
};
Ribbon.prototype.tick = function (c) {
var _this = this;
if (c % 2 == 0) {
// render 30fps
this.rootPos += this.speed.rest;
this.updateRibbon();
}
requestAnimationFrame(function () {
_this.tick(c + 1);
});
};
Ribbon.prototype.toogleState = function () {
this.setState(this.currentState.type == 'home' ? this.states.selected : this.states.home);
};
Ribbon.prototype.selectBlock = function (blockId) {
if (blockId === 'back')
return this.deselectBlock();
var selectedBlockPosition = 100;
for (var i in this.blocks) {
if (this.blocks[i].id === blockId || this.blocks[i].type === 'back') {
selectedBlockPosition = this.blocks[i].position.current;
this.blocks[i].position.target = this.blocks[i].type === 'back' ? 10 : 75;
}
else {
this.blocks[i].position.target = this.blocks[i].position.current < selectedBlockPosition ? this.blocks[i].position.home - 100 : this.blocks[i].position.home + 100;
}
}
this.setState(this.states.selected);
};
Ribbon.prototype.deselectBlock = function () {
for (var i in this.blocks) {
this.blocks[i].position.target = this.blocks[i].type == 'menu' ? this.blocks[i].position.home : -100;
}
this.setState(this.states.home);
};
Ribbon.prototype.setState = function (newState) {
var transitionSpeed = 2;
var ease = Power2.easeInOut;
this.currentState.type = newState.type;
TweenMax.to(this.currentState.start, this.speed.changeState, { top: newState.start.top, left: newState.start.left, thickness: newState.start.thickness, ease: ease });
TweenMax.to(this.currentState.end, this.speed.changeState, { top: newState.end.top, left: newState.end.left, thickness: newState.end.thickness, ease: ease });
TweenMax.to(this.currentState.wave, this.speed.changeState, { amplitude: newState.wave.amplitude, frequency: newState.wave.frequency, ease: ease });
if (this.currentState.wave.transitionSpeed > this.speed.rest) {
TweenMax.to(this.speed, this.speed.changeState / 2, { rest: newState.wave.transitionSpeed, ease: Power2.easeIn });
TweenMax.to(this.speed, this.speed.changeState / 2, { rest: this.defaultRestSpeed, delay: this.speed.changeState / 2, ease: Power2.easeOut });
}
else {
TweenMax.to(this.speed, this.speed.changeState, { rest: this.defaultRestSpeed, ease: ease });
}
for (var i = 0; i < this.blocks.length; i++) {
var block = this.blocks[i];
if (block.position.current !== block.position.target)
TweenMax.to(block.position, this.speed.changeState, { current: block.position.target, ease: ease });
}
};
return Ribbon;
}());
var ribbon = new Ribbon(Snap('#svg'));
ribbon.init();
@import 'https://fonts.googleapis.com/css?family=Catamaran';
html, body
{
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: 'Catamaran', sans-serif;
}
body {
background: #eee;
background: linear-gradient(to left, #ddd , #eee);
}
.container
{
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
svg
{
z-index: 10;
}
.block
{
cursor: pointer;
text
{
fill: white;
}
polygon
{
fill: white;
opacity: 0.3;
transition: opacity 0.3s ease;
}
&:hover
{
polygon
{
opacity: 0.5;
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<meta charset="utf-8"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<links href="style.css" type="text/css" rel="stylesheet">
</head>
<body>
<div class="container">
<script src="script.js"></script>
<svg id="svg"></svg>
</div>
</body>
</html>
由于某些原因,它确实可以在这里工作,但Firefox dev版本告诉我以下TypeError: this.svg is null.
script.js:85:9
,我似乎无法解决任何问题。对于任何想知道我的朋友想出这个的人,对他而言,它的工作非常好,但他也不知道我能做什么。在回答时请记住,我是新手,所以如果它真的很愚蠢的话,请放轻松。
答案 0 :(得分:0)
我真的不明白你在这里想做什么但是,Snap方法没有返回任何内容,因为脚本先运行,所以svg标签由于某种原因被删除了。
对HTML进行了轻微修改,它完美无缺。
<html lang="en">
<head>
<meta charset="utf-8"/>
<links href="style.css" type="text/css" rel="stylesheet">
</head>
<body>
<div class="container">
<svg id="svg"></svg>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script><script src="script.js"></script>
</body>
</html>