当看到这个活动【🏮中秋创意投稿,共赏明月光芒 | 创意投稿大赛】的时候,我想了想什么是创意。
- 小游戏类:月饼制作、嫦娥奔月、猜灯谜、兔子寻宝、数字拼图等;
- 互动贺卡:可以设计角色、场景和动画效果,并通过代码控制动画的播放和交互;
- 可爱动画:使用编程语言和动画技术,创作一个中秋节传统故事的动画短片;
- 中秋元素:用编程语言实现各类中秋元素制作
我又想了想最近一直在更新的d3文章。好家伙,直接复制一份海报不就完事了?说干就干。
这是官方海报:
这是我用de复刻的效果:
模仿的很到位,不能说完全一样,那是基本不一样。不过我要的事是创意,那兔子,那月饼,那花花草草,d3我是写不出来的,咱就写点能写的吧。什么能写?文字能写,月亮能写,渐变能写。兔子🐰灯笼🏮月饼🥮也能写,不过是文字不是path。还要加动效。
- 月饼和灯笼运动起来,我感觉月饼往下运动合理一些,不然都飘走了我吃啥?
- 中间的主文字立体起来,给我摇摆起来。
- 月亮加发光效果。
- 嫦娥(场景唯一的图片素材。AI生成的)元素必须加上,当他靠近月亮的时候,月亮发光更大。
- 小兔子也跳起来。
确定了开发内容就开始敲代码。 下面是全部代码。
let width = 1000;
let height = 563;
const svg = d3.select('#svg').append('svg');
svg.attr('width', width).attr('height', height)
.style('position', 'absolute')
.style('top', '50%')
.style('left', '50%')
.style('transform', 'translate(-50%,-50%)')
.style('background', '#262A4F')
// 4F868A 渐变色
svg.append('defs').html(`
<linearGradient id="linear" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#4F868A" stop-opacity="0"></stop>
<stop offset="100%" stop-color="#4F868A" stop-opacity="1"></stop>
</linearGradient>
`)
// 月亮的光芒
let moonlight = svg.append('defs')
.append('filter')
.attr('id', 'moonlight')
.append('feGaussianBlur')
.attr('in', 'SourceGraphic')
.attr('stdDeviation', 6)
.attr('result', 'moonlight')
// 一个渐变的矩形 1000*563 位于底部偏上
svg.append('rect')
.attr('x', 0)
.attr('y', height * 0.6)
.attr('width', width)
.attr('height', height * 0.27)
.attr('fill', 'url(#linear)')
//🐇
let gTu = svg.append('g')
.attr('transform', `translate(${width},${height * 0.87})`)
let rabbit = gTu.append('text')
.attr('font-size', 60)
.text('🐇')
// 兔子动画
rabbit
.transition()
.duration(500)
.attr('transform', `translate(0,-30)`)
.ease(d3.easePolyOut)
.transition()
.duration(400)
.attr('transform', `translate(0,0)`)
.ease(d3.easePolyIn)
.on('end', function () {
d3.select(this).transition()
.duration(500)
.attr('transform', `translate(0,-30)`)
.ease(d3.easePolyOut)
.transition()
.duration(400)
.attr('transform', `translate(0,0)`)
.ease(d3.easePolyIn)
.on('end', arguments.callee)
})
let _t = width;
setInterval(() => {
_t -= 0.5;
gTu.attr('transform', `translate(${_t},${height * 0.87})`)
if (_t < -100) {
_t = width;
}
}, 10);
let gTitle = svg.append('g')
// 中秋创意投稿 共赏明月光芒
let text1 = gTitle.append('text')
.attr('x', width / 2)
.attr('y', height * 0.32)
.attr('text-anchor', 'middle')
.attr('font-size', 86)
.attr('fill', '#FBE77B')
.attr('font-family', 'Microsoft YaHei')
.attr('font-weight', 'bold')
.text('中秋创意投稿')
let text2 = gTitle.append('text')
.attr('x', width / 2 - 20)
.attr('y', height * 0.49)
.attr('text-anchor', 'middle')
.attr('font-size', 86)
.attr('fill', '#FBE77B')
.attr('font-family', 'Microsoft YaHei')
.attr('font-weight', 'bold')
.text('共赏明月光芒')
//模拟立体效果
let gTitles = [];
for (let i = 0; i < 15; i++) {
let g = gTitle.clone(true);
g.attr('transform', `translate(${i * 1},${i * 1})`)
g.selectAll('text')
.attr('fill', '#50707B')
if (i == 0) {
g.selectAll('text')
.attr('fill', '#FBE77B')
}
if (i == 14) {
g.selectAll('text')
.attr('filter', 'url(#moonlight)')
}
gTitles.push(g)
}
// 动态改变gTitle的位置
let gTitleX = 0;
setInterval(() => {
for (let i = 0; i < 15; i++) {
gTitles[i].attr('transform', `translate(${i * Math.sin(gTitleX)},${i * Math.cos(gTitleX)})`)
}
gTitleX += 0.01;
}, 10);
let _gctitle = svg.append('g')
.attr('transform', `translate(${width / 2},${height * 0.6 + 50})`);
let gctitle = _gctitle.append('g')
//代码舞动仪式感
let text3 = gctitle.append('text')
.attr('text-anchor', 'middle')
.attr('font-size', 32)
.attr('fill', '#F6E07D')
.attr('font-family', 'Microsoft YaHei')
.attr('font-weight', 'bold')
.text('代码舞动仪式感')
let text3Width = text3.node().getBBox().width;
let text3Height = text3.node().getBBox().height;
let text3X = text3.node().getBBox().x;
let text3Y = text3.node().getBBox().y;
let dy = 2
let text3Rect = gctitle.append('rect')
.attr('x', text3X - 30)
.attr('y', text3Y - dy)
.attr('width', text3Width + 60)
.attr('height', text3Height + 8)
.attr('fill', '#F4BA5E')
let text3Triangle1 = gctitle.append('polygon')
.attr('points', `${text3X - 30},${text3Y - dy} ${text3X - 30},${text3Y - dy + text3Height + 8} ${text3X - 30 - 30},${text3Y - dy + text3Height / 2 + 4}`)
.attr('fill', '#F4BA5E')
let text3Triangle2 = gctitle.append('polygon')
.attr('points', `${text3X + text3Width + 30},${text3Y - dy} ${text3X + text3Width + 30},${text3Y - dy + text3Height + 8} ${text3X + text3Width + 30 + 30},${text3Y - dy + text3Height / 2 + 4}`)
.attr('fill', '#F4BA5E')
let text4 = gctitle.append('text')
.attr('text-anchor', 'middle')
.attr('font-size', 32)
.attr('fill', '#252A48')
.attr('font-family', 'Microsoft YaHei')
.attr('font-weight', 'bold')
.text('代码舞动仪式感')
//活动时间:2023年9月 12日-2023年9月24日
let text5 = svg.append('text')
.attr('x', width / 2)
.attr('y', height * 0.6 + 50 + 60)
.attr('text-anchor', 'middle')
.attr('font-size', 22)
.attr('fill', '#FFFFFF')
.attr('font-family', 'Microsoft YaHei')
.attr('font-weight', 'bold')
.text('活动时间:2023年9月12日-2023年9月24日')
// 左上角
let gltitle = svg.append('g')
.attr('transform', `translate(${width * 0.08},${height * 0.12})`)
let ltitle = gltitle.append('text')
.attr('x', -6)
.attr('y', 0)
.attr('font-size', 28)
.attr('fill', '#FFFFFF')
.attr('font-family', 'Microsoft YaHei')
.attr('dominant-baseline', 'middle')
.text('稀士掘金')
gltitle.append('rect')
.attr('width', 4)
.attr('height', 4)
.attr('fill', '#FFFFFF')
.attr('transform', `translate(-28,-15) rotate(45) `)
gltitle.append('path')
.attr('d', 'M-8,0 L0,8 L8,0')
.attr('fill', 'none')
.attr('stroke', '#FFFFFF')
.attr('stroke-width', 3)
.attr('transform', `translate(-28,-10)`)
gltitle.append('path')
.attr('d', 'M-12,0 L0,12 L12,0')
.attr('fill', 'none')
.attr('stroke', '#FFFFFF')
.attr('stroke-width', 3)
.attr('transform', `translate(-28,-5)`)
//Data_Adventure
gltitle.append('text')
.attr('x', -6)
.attr('y', 30)
.attr('font-size', 16)
.attr('fill', '#FFFFFF80')
.attr('font-family', 'Microsoft YaHei')
.attr('dominant-baseline', 'middle')
.text('Data_Adventure')
// 月亮
let moonRadius = 65;
let moonX = width - moonRadius * 2.55;
let moonY = moonRadius * 0.6
let moon = svg.append('circle')
.attr('cx', moonX)
.attr('cy', moonY)
.attr('r', moonRadius)
.attr('fill', '#F6E07D')
.attr('filter', 'url(#moonlight)')
.attr('class', 'moon');
let moon1 = svg.append('circle')
.attr('cx', moonX)
.attr('cy', moonY)
.attr('r', moonRadius)
.attr('fill', '#F6E07D')
.attr('filter', 'url(#moonlight)')
.attr('class', 'moon')
.attr('opacity', 0)
let moon2 = svg.append('circle')
.attr('cx', moonX)
.attr('cy', moonY)
.attr('r', moonRadius)
.attr('fill', '#F6E07D')
.attr('class', 'moon')
// 🏮灯笼 text
let gdeng = svg.append('g')
// 随机生成灯笼
let dengs = [];
for (let i = 0; i < 88; i++) {
let x = Math.random() * width;
let y = Math.random() * height;
let _k = Math.random();
let speed = Math.random() * 0.5 + 0.5;
let deng = gdeng.append('text')
.attr('font-size', 28)
.attr('fill', '#FF780C')
.attr('font-family', 'Microsoft YaHei')
.attr('dominant-baseline', 'middle')
.text(() => _k > 0.5 ? '🏮' : '🥮')
.attr('opacity', 0)
.attr('transform', `translate(${x},${y})`)
dengs.push({
x, y, speed, deng
})
}
setInterval(() => {
for (let i = 0; i < dengs.length; i++) {
let deng = dengs[i];
if (deng.y < -100) {
deng.x = Math.random() * width;
deng.y = height + 100;
deng.speed = Math.random() * 0.5 + 0.5;
}
deng.y -= deng.speed;
deng.deng.attr('transform', `translate(${deng.x},${deng.y})`)
deng.deng.attr('opacity', 1)
}
}, 10);
// 嫦娥出场了
let defs = svg.append('defs');
let pattern = defs.append('pattern')
.attr('id', 'bg')
.attr('x', '0%')
.attr('y', '0%')
.attr('height', '100%')
.attr('width', '100%')
.attr('viewBox', `0 0 ${100} ${100}`)
.append('image')
.attr('x', '0%')
.attr('y', '0%')
.attr('height', 100)
.attr('width', 100)
.attr('xlink:href', './e.png')
let Change = svg.append('circle')
.attr('cx', width/2)
.attr('cy', height/2)
.attr('r', moonRadius)
.attr('fill', 'url(#bg)')
.attr('class', 'moon')
// 鼠标事件
svg.on('mousemove', function () {
let x = d3.event.offsetX;
let y = d3.event.offsetY;
Change.attr('cx', x).attr('opacity', 1)
.attr('cy', y)
if (Math.abs(x - moonX) < moonRadius * 2 && Math.abs(y - moonY) < moonRadius * 2) {
moon1.attr('opacity', 1)
} else {
moon1.attr('opacity', 0)
}
})
// 鼠标移出
svg.on('mouseout', function () {
Change.attr('opacity', 0)
})
补充一张素材图,码上用,希望能用。
在线效果: